Map接口
1、Map接口基本情况
-
Map用于保存具有映射关系的数据(Set集合也有这样的特点,只不过value的值是规定好的)
-
Map中的key和value可以是任意数据类型的,会封装到HashMap$Node对象中;
-
Map中的key是不允许重复的,原因和HashSet一样
-
Map 中存入相同的key值元素,会产生替换的效果,将原来的key值得数据进行替换
Map map = new HashMap(); map.put("001","jack"); map.put("002","tom"); map.put("001","jerry"); System.out.println(map); // {001=jerry, 002=tom}
-
Map中的value是可以重复的,
-
Map中key可以为null,value也可以为null,注意key为null的只有一个,value可以有多个null值
-
常用String作为Map中的key其他类型可以,因为他是Object类型的
-
key 和value直接存在单一对应关系,既可以通过key值总能找到对应的value值
-
map接口中的常用方法:
- put(); 添加
- remove方法:
- get()
- isEmpty
- size()
- clear()
- containsKey()
2、遍历方式:
1、先取出所有的key,在通过key取出对应的value
-
增强for循环
Map map = new HashMap(); map.put("122",12); map.put("123",13); map.put("124",14); Set set = map.keySet(); for(Object item : set){ sout(item + "-" + map.get(item)); }
-
迭代器
Map map = new HashMap(); map.put("122",12); map.put("123",13); map.put("124",14); Set set = map.keySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object key = iterator.next(); System.out.println(key + "-" + map.get(key)); }
2、将所有的value值取出
-
增强for
Map map = new HashMap(); map.put("122",12); map.put("123",13); map.put("124",14); Collection values = map.values(); for (Object item : values) { System.out.println(item); }
-
迭代器
Map map = new HashMap(); map.put("122",12); map.put("123",13); map.put("124",14); Collection values = map.values(); Iterator iterator = values.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); }
3、通过entrySet方法获取获取一个类型为Map.Entry的集合
-
增强for
Map map = new HashMap(); map.put("122",12); map.put("123",13); map.put("124",14); Set set = map.entrySet(); for (Object entry : set) { Map.Entry item = (Map.Entry) entry; System.out.println(item.getKey() + " " + item.getValue()); }
-
迭代器
Map map = new HashMap(); map.put("122",12); map.put("123",13); map.put("124",14); Set set = map.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); System.out.println(entry.getKey() + " " + entry.getValue()); }
3、HashMap集合
扩容机制
-
HashMap底层维护了一个Node类型的数组table,默认为null
-
当创建对象时,加载因子初始化为0.75,
-
当添加key -value时,通过key的哈希值,得到在table中的索引然后判断该索引处是否有元素,如果没有元素直接添加,如果该索引处有元素,继续判断元素元素的key和准备加入的key值是否相等,则直接替换为value,如果不同需要判断是树结构还是链表结构,做出相应的处理,如果添加是发现容量不够,则需要扩容
-
第一次添加,需要table容量为16,临界值为12,(16 * 0.75)
-
以后再次扩容是,按照原来的两倍(32),临界值为(32 * 0.75) = 24;
-
在java8 中如果一条链表的元素超过8个,并且table的大小,大于等于64,就会进行树化(红黑树)
-
代码解读
/* * 代码解读 * * 1、执行构造方法: new HashMap(); * 初始化加载因子 this.loadFactor = DEFAULT_LOAD_FACTOR; (0.75) * HashMap$Node[] table = null; * * 2、执行put (调用hash方法,计算key的值 hash值(return (key == null) ? 0 : (h = *key.hashCode()) ^ (h >>> 16);)) * public V put(K key, V value) { * return putVal(hash(key), key, value, false, true); * } * * 3、执行putval方法(最核心代码) * * final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //判断table为null 或者size为0 是扩容为16; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } * * 会存在减枝的操作 * */
注意:
- 扩容是(开始为16),如果在某一条链上的个数等于8是,在该链上加一个,会将table容量为32,在加一个,或扩容为64,在加一个就会转化为红黑树;
4、HashTable集合
1、基本情况
- 存放的是键值对
- hashtable的键和值不能为null,
- hashtable使用方法基本上和HashMap一样
- HashTable是线程安全,HashMap非线程安全
2、底层结构
-
.底层是一个数组 + 链表
-
初始化的长度为11;
-
临界值为0.75 =* 11 = 8;
-
扩容:
第一次为11;
第二次为11 * 2 + 1 = 23;
5、HashMap和HashTable的比较
HashMap | HashTable | |
---|---|---|
版本 | 1.2 | 1.0 |
线程安全 | 非线程安全 | 线程安全 |
效率 | 高 | 较低 |
允许null键和null值 | 可以 | 不可以 |
初始的容量和每次扩容的大小不同 | 默认为null,第一次添加,初始为16,扩容为原来的2倍 | 默认为长度为11,扩容为原来的两倍 + 1; |
计算Hash值得方式不同 | 是使用自定义的哈希算法。 | 没有自定义哈希算法,而直接采用的key的hashCode() |
6、TreeMap
- 在构造方法中可以传入比较器
- 可以进行排序
- 存放的是键值对
- 基于红黑树对所有的的key进行排序
- 自然排序和定值排序
- 底层是红黑树