1、Java常见的集合
Java的集合主要由Collection和Map派生,其中Collection派生出了List、Queue、Set,List下面又有ArrayLiist、LinkedList和Vector等,Vector派生出Stack,Queue有Deque,Set派生出HashSet、SortedSet等;Map派生出HashMap、HashTable和SortedMap等。
2、List、Set、Queue和Map的区别以及应用场景
List是存储元素时有序可重复。
Set存储元素时无序不重复,用于去重。
Queue是先进先出的队列
Map是存储键值对,其中Set的底层是Map,Set中的元素值就是Map的键值。
3、ArrayList和LinkedList
ArrayList的底层是动态数组,在扩容的时候是创建一个原数组长度1.5倍大小的数组,然后将原数组的内容复制进去。LinkedList是基于链表实现的。ArrayList适用于查找,LinkedList适用于增删。
// 在遍历时移除ArrayList中某个元素
Iterator iter = list.iterator();
whiile(iter.hasNext()) {
if (iter.next.equals("xxx")) {
iter.remove();
}
}
4、HashMap
HashMap的底层是数组+链表+红黑树,当链表长度大于8并且数组长度大于64时会转化为红黑树,当红黑树元素小于6时又会转化为链表。
4.1 为什么要用到链表?
因为在put元素进去的时候会存在hash冲突,HashMap采用链地址法来解决hash冲突, 所以用到了链表。
4.2 为什么用红黑树?
当链表比较长的时候,查询效率会变慢,用红黑树能够提高效率。
4.3 既然红黑树效率比链表高,为什么不直接用红黑树?
当元素较少的时候,红黑树有左旋右旋的操作会影响效率,此外树节点空间占的比普通节点大。
4.4 HashMap的put流程
首先计算key的hashCode的值,之后进行高十六位和低十六位进行异或运算,这样是为了尽量减少hash冲突,然后将运算结果对数组长度减一进行取模操作得到key的索引。判断索引处有没有元素,如果没有就直接put进去,如果有会判断是红黑树还是链表,如果是链表就遍历元素采用尾插法将其插入进去,否则以红黑树的形式插入。如果链表长度大于8了,就将链表转化为红黑树。最后检查是否需要扩容。
4.5 HashMap的扩容过程
当map中元素个数大于threshold,也就是大于负载因子*容量大小的时候,就会触发扩容操作。HashMap会创建一个原数组两倍大小的数组,然后将元素以尾插法的方式插入到新数组中。
4.6 HashMap数组长度为什么是2的幂次方
在put元素的时候,会计算hashcode值然后高低16位异或操作后进行取模运算。在这里取模运算使用的是将异或结果和数组长度减一进行与操作来实现。用位运算可以提高运行的速度,使用2的幂次方也可以不浪费数组空间。
4.7 HashMap和HashTable的区别
HashMap线程不安全,可以用ConcurrentHashMap替代;HashTable线程安全
HashMap效率一般比HashTable高
HashMap可以接收值为null的key和value,但HashTable不行
HashTable是直接使用key的hashcode值,但HashMap对hashcode值进行了高低十六位异或操作
4.8 HashMap为什么线程不安全
可能会存在数据覆盖的情况。在多线程环境下,当put元素A的时候,假设刚好找到A的索引位置还没put进去,这时候另一个操作要put元素B,恰巧位置和A一样,当元素B放进去之后在进行元素A的操作,这个时候已经判断好了位置,就会直接放到相应的位置,从而覆盖了元素B。
5、哪些集合线程安全哪些不安全
线程安全的集合有:Vector、Stack、HashTable和ConcurrentHashMap
线程不安全的集合有很多:HashMap、ArrayList、LinkedList...
6、ConcurrentHashMap
ConcurrentHashMap在put的时候采用Synchronized锁住当前链表或者树的首节点来保证线程安全,相比之前锁粒度更小。之前是分段锁,锁住的是数组中的一段。