Map双列集合结构
Map是采用key-value键值对存储数据的,一个key对应一个值,对于key不可重复,value可以重复,主要的实现类有HashMap,LinkedHashMap,TreeMap,HashTable
HashMap
Map集合中最常用的实现类
常用方法
- 添加功能:
V put(K key,V value):添加元素(也可以实现修改功能) - 删除功能:
void clear():清空所键值对元素
V remove(Object key):根据键移除对应的值,并把值返回 - 判断功能:
containsKey(Object key):是否包含指定的键
containsValue(Object value):是否包含指定的值
isEmpty():是否为空 - 遍历功能:
Set<Map.Entry<K,V>> entrySet():获取键值对
V get(Object key):根据键获取值
Collection values():获取所值的集合 - 获取功能:
Set keySet():获取所键的集合
int size():获取集合元素的个数
使用这些方法
//创建map集合
HashMap<Integer, String> map = new HashMap<>();
//添加元素
map.put(1, "a");
map.put(2, "b");
map.put(3, "C");
//键有重复值发生覆盖
map.put(3, "C");
//通过键删除整个键值对
//map.remove(1);
//清空
//map.clear();
//判断是否为空
System.out.println(map.isEmpty());//f
//是否包含key = 4
System.out.println(map.containsKey(4));//f
//是否包含"b"
System.out.println(map.containsValue("b"));//t
//集合元素个数
System.out.println(map.size());//3
//通过键获取值
System.out.println(map.get(3));//C
//获取所有值组成的集合,打印输出
Collection<String> s = map.values();
for (String value : s) {
System.out.println(value);
}
System.out.println(map);
}
Map常见的遍历方式
第一种遍历方式:最常用的遍历方式,for循环中使用entries实现遍历
HashMap<String, Integer> map = new HashMap<>();
map.put("张三", 12);
map.put("李四", 34);
map.put("王五", 24);
//第一种遍历方式
for(Map.Entry<String, Integer> entry: map.entrySet()) {
//获取key
String key = entry.getKey();
//获取value
Integer value = entry.getValue();
System.out.println(key + "--" + value);
}
第二种方式:使用迭代器进行遍历
HashMap<String, Integer> map = new HashMap<>();
map.put("张三", 12);
map.put("李四", 34);
map.put("王五", 24);
//使用迭代器进行遍历
Iterator<Entry<String, Integer>> it = map.entrySet().iterator();
while(it.hasNext()) {
Entry<String, Integer> en = it.next();
String key = en.getKey();
Integer value = en.getValue();
System.out.println(key+"---"+value);
}
第三种方式:分开遍历key与value,只需要key或者是value来使用
// TODO Auto-generated method stub
HashMap<String, Integer> map = new HashMap<>();
map.put("张三", 12);
map.put("李四", 34);
map.put("王五", 24);
//第三种方式遍历只需要key或者value
for(String key:map.keySet()) {
System.out.println(key);
}
for(Integer value: map.values()) {
System.out.println(value);
}
HashMap底层实现原理
JDK1.8之前 数组+链表
- 实例化HashMap之后,底层先创建长度为16的Entry[] table的数组
- 首先调用key1所在类的hashCode()方法计算出key1的哈希值,经过一系列的算法之后得到在数组中的存放位置,如果位置是空则添加成功
- 如果不为空,意味着此位置存在一个或者多个数据(链表形式存储),那key1的哈希值与这些数据的哈希值进行比较:
- 如果key1的哈希值与已存在的数据的哈希值不同则key1-value1添加成功
- 若哈希值相同,调用key1所在类中equals方法:
- 方法返回false,key1-value1添加成功
- 方法返回true,value1覆盖value2的值
当然在不断的添加之后,会启动动态扩容机制,扩容于原来的两倍,并复制原来的数组到新数组中
JDK1.8之后 数组+链表,容量大于8使用红黑树
-
创建HashMap对象的时候,底层没有创建一个长度为16的数组,而是在调用put()的方法才会创建长度为16的数组
-
底层的数组是Node[] 不是Entry[]
-
链表的长度大于8时将采用链表转换为红黑树进行存储数据
扫描二维码关注公众号,回复: 11931645 查看本文章形成链表时,1.8以前新的数据指向旧的数据,1.8以后是旧的元素指向新的元素
HashMap 和 Hashtable的异同?
- 线程安全方面:前者不安全,后者安全
- 键值是否为null:前者可以为null,后者不可以为null
LinkedHashMap
LinkedHashMap主要继承HashMap用法上基本一样,主要区别就是LinkedHashMap内部提供了Entry,替换HashMap中的Node.
HashMap中的内部类
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
LinkedHashMap的内部类
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
TreeMap
元素可以根据键进行排序,元素具唯一性
排序方式(基本与TreeSet相同):
自然排序:让元素所在的类实现自然排序接口Comparable
比较器排序:让集合的构造方法接收一个比较器接口(Comparator的实现类对象)