一、接口Map
1.1 Map集合特点
1.Map集合和Collection没有任何关系
2.Map集合以key和value的这种键值对的方式存储数据
3.key和value都是存储Java对象的内存地址
4.所有Map集合的一大特点就是:无需不可重复
5.Map集合的key和set集合存储元素特点相同
1.2 Map集合中的常用方法
void clear()
从该地图中删除所有的映射(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回 true 。
boolean containsValue(Object value)
如果此Map将一个或多个键映射到指定的值,则返回 true 。
V get(Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()
如果此Map不包含键值映射,则返回 true 。
Set<K> keySet()
返回此Map中包含的键的Set视图。
V put(K key, V value)
将指定的值与该映射中的指定键相关联(可选操作)。
V remove(Object key)
如果存在(从可选的操作),从该地图中删除一个键的映射。
Collection<V> values()
返回此地图中包含的值的Collection视图。
Set<Map.Entry<K,V>> entrySet()
返回此地图中包含的映射的Set视图。
Map<Integer,String> map = new HashMap();
map.put(1, "老王");
map.put(2, "老王");
map.put(3, "老张");
map.put(4, "李四");
System.out.println(map.get(1));
System.out.println(map.size());
map.remove(2);
for (Integer key : map.keySet()) {
System.out.println(key);
}
System.out.println(map.containsKey(4));
System.out.println(map.containsKey(new Integer(4)));
System.out.println(map.containsValue("老王"));
System.out.println(map.containsValue(new String("老王")));
Collection<String> values = map.values();
System.out.println(values);
Set set = map.keySet();
for (Object o : set) {
System.out.println(o);
}
1.3 遍历Map集合的几种方式
1.3.1 通过迭代器遍历
Set<Integer> keys = map.keySet();
Iterator<Integer> iterator = keys.iterator();
while (iterator.hasNext()){
Integer key = iterator.next();
String value = map.get(key);
System.out.println("key = " + key + "," + "value = " + value);
}
1.3.2 通过 entrySet()方法遍历
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<Integer, String> next = iterator.next();
System.out.println(next);
}
1.3.3 通过增强for循环遍历
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
for (Map.Entry<Integer, String> node : entrySet) {
Integer key = node.getKey();
String value = node.getValue();
System.out.println(key + "=" + value);
}
Set<Integer> keys = map.keySet();
for (Integer key : keys) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
二、HashMap
2.1 HashMap概述
- HashMap底层是哈希表/散列表数据结构
- 有关哈希表的数据结构:
- 哈希表是一个数组和单向链表的结合体
- 哈希表将两种数据结构融合在一起,充分发挥各自的优点
- 有关HashMap底层源码:
- HashMap底层实际上就是数组:transient Node<K,V>[] table;
- 静态内部类HashMap.Node:static class Node<K,V> implements Map.Entry<K,V>
- final int hash;哈希值(哈希值是key的hashCode()方法的执行结果,hash值通过hash算法获得,可以转化为数组下标)
- final K key;存储到Map集合中的key
- V value;存储到Map集合中的value
- Node<K,V> next;下一个内存节点的地址
2.2 哈希表的数据结构
2.2.1 Map方法:put(K key, V value) 实现原理
- 将key与value封装到Node对象中区,底层调用key的hashCode()方法得出hash值,通过哈希算法将hash值转化为数组的下标,如果下标没有任何元素,那么就把Node添加到这个位置上,如果下标对应的位置上有链表,此时会把key值与链表上的每一个节点中的key值进行equals()比较,如果返回false,这个新节点就被添加到链表末尾,如果返回的是true,这个节点的value将会被覆盖
2.2.2 Map方法:get(Object key) 实现原理
- 首先调用key的hashCode()方法得出哈希值,通过哈希算法转化为数组下标,通过数组下标快速定位到某个位置,如果这个位置没有元素则返回null,如果这个位置上有单向链表,那么会用key和单向链表中的key进行equals()比较,如果equals返回false,那么get方法返回null,如果equals()返回true,那么这个节点的value就是我们要寻找的value,get()方法会最终返回到这个value值
2.2.3 哈希碰撞
- 如果o1与o2的hash值相同,一定是放到同一链表上
- 如果o1与o2的hash值不同,但由于哈希算法执行结束后转换的数组下标可能相同,此时会发生“哈希碰撞”
2.2.4 总结
- HashMap集合的key无序,不可重复,equals()方法保证了HashMap集合中的key不可重复,如果key重复了,value值会被覆盖掉
- HashMap集合key部分的元素其实就是放到HashSet集合中,所以HashSet集合中的元素与HashMap集合中key部分的元素需要重写hashCode()与equals()方法
- 哈希表HashMap使用不当无法发挥其性能,如果将所有的hashCode()方法返回固定值,那么会导致底层哈希表编程纯单向链表,这种情况我们便称为:散列不均匀。如果hashCode()返回值都设定为不一样的值,那么哈希表就成了一维数组,也是一种散列不均匀
- HashMap集合的
默认初始化容量是16,且初始化容量必须是2的倍数 ,默认的加载因子是0.75 ,底层数组的容量达到75%的时候开始扩容,每次扩容为原容量的二倍,注意:【HashMap集合初始化容量必须是2的倍数】 - HashMap集合中的key与value允许null
2.3 JDK8后的版本对HashMap的优化
- 在JDK8以后版本中,如果哈希表单向链表中的元素 超过8个 ,单向链表这种数据结构会变成 红黑树数据结构 ,当 红黑树上的节点小于6个 的时候,会 重新把红黑树变成单向链表数据结构,这种方式提高了检索效率
三 、关于HashCode()与equals()方法
Student s1 = new Student("张三");
Student s2 = new Student("张三");
System.out.println(s1.equals(s2));
System.out.println("s1的hashCode:" + s1.hashCode());
System.out.println("s2的hashCode:" + s2.hashCode());
- 在Map集合中的存取过程,都是先调用hashCode()方法,然后再调用equals()方法
- 在put(K key, V value) 方法中,K.hashCode()方法返回哈希值,哈希值经过哈希算法转换为数组下标,如果数组下标位置上是null,equals()方法就不需要执行
- 在==get(Object key)==方法中,K.hashCode()方法返回哈希值,哈希值经过哈希算法转换为数组下标,如果 数组下标位置上是null,equals()方法就不需要执行
四、HashTable集合
- HashTable的key与value值都不能为null ,而HashMap的key与value可以为null
- HashTable方法都带有synchronized,所以都是 线程安全 的
- HashTable与HashMap地城都是哈希表数据结构,HashTable初始容量为11,扩容因子是0.75,每次扩容为【原容量 * 2 + 1】
五、TreeMap集合
- TreeMap集合底层是二叉树
- TreeSet集合放入的数据其实放入了一个TreeMap集合中
- TreeMap与TreeSet集合是可排序集合,集合中的元素是自动按照大小顺序排序的,不可排序自定义类型