HashMap的基本特性
- HashMap是线程不安全的;
- HashMap的底层主要是基于数组和链表实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储位置的
- HashMap的遍历方式
-
HashMap<Integer,Integer> hashMap = new HashMap<>(); hashMap.put(1,2); hashMap.put(2,4); //第一种:普通使用,二次取值(性能差) for (Integer key:hashMap.keySet()){ System.out.println(key); } //第二种:性能比第一种好,一次取值 Iterator<Map.Entry<Integer, Integer>> iterator = hashMap.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry<Integer, Integer> next = iterator.next(); System.out.println(next.getKey()+next.getValue()); } //第三种:推荐使用,尤其是容量大的 for(Map.Entry<Integer,Integer> entry:hashMap.entrySet()){ System.out.println(entry.getKey()+entry.getValue()); } //第四种: for (Integer value:hashMap.values()){ System.out.println(value); } //第五种:java8使用foreach进行遍历 hashMap.forEach((k,v) -> System.out.println(k+v));
-
- 扩容机制:
- HashMap默认的负载因子为0.75f,默认初始大小为16;
- 数组大小*负载因子=要调整大小的值(临界值)
- 从源代码来看HashMap,在添加时执行扩容方法,执行扩容方法时必须两个条件同时相等,(链表的总条数>=要调整大小的值)和当前要添加的值的索引位置不为null(也就是说当前要添加值得索引位置里面有值),才会执行扩容方法,扩容的大小为原来的数组长度乘以2(原来的数组长度的二倍)。扩容之后所有元素还会根据key来重新计算hash值进行重新添加元素,重新执行顺序为下标从0开始,里面的链表从外层开始。
- 数组结构:
- Hash Map底层就是一个数组,数组中的每一项又是一个链表,新建一个HashMap的时候,就会初始化一个数组
HashMap和HashTable的区别?
HashMap和HashTable都实现了Map接口,主要区别有线程安全性,同步(synchronize),以及速度。
1.HashMap几乎等价于HashTable,除了Hash Map是非synchronize的,并可以接受null(Hash Map可以接受为null的键值和key值,而HashTable则不行。
2.HashMap是非synchronized,而HashTable是synchronized,这意味着HashTable是线程安全的,多个线程可以共享一个HashTable;而如果没有正确的同步的话,多个线程是不能共享HashMap的,Java5提供了ConcurrentHashMap,他是HashTable的替代,比HashTable的扩展性更好。
3.另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而HashTable的enumerator迭代器不是fail-fast的,所以当有其他线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但是这并不是一个一定发生的行为,要看JVM,这条同样也是Enumeration和Iterator的区别。
4.由于HashTable是线程安全也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过HashTable
5.HashMap不能保证随着时间的推移Map中的元素次序是不变的。