1、ArrayList与LinkedList异同
1)、底层结构,ArrayList底层是数组,LinkedList底层是双向链表数据结构
2)、ArrayList查询速度快,增删速度慢;LinkedList查询速度慢,增删速度快
3)、ArrayList可以随机访问(实现了RandomAccess接口),LinkedList不能随机访问
4)、内存空间占用:ArrayList的空间浪费主要体现list列表的结尾会预留一定的容量空间,而LinkedList相对于ArrayList相同元素要占用更多的空间(存前后指针)
5)、两个都是线程不安全的
补充说明
1)list遍历方式的选择
1、实现了RandomAccess接口的list,优先选择普通for循环,其次是foreach
2、未实现RandomAccess接口的list,优先选择iterator遍历,大的数据千万别使用for循环
2)双向链表(LinkedList)
1、它的每个数据结点都有两个指针,一个指向前驱,一个指向后继
2、ArrayList与Vector的区别
1)ArrayList不同步,Vector同步
2)ArrayList效率高,Vector效率低
3、hashmap的底层实现
hashmap是一个用于存储key-value键值对的集合,每一个键值对也叫做entry,这些entry分散在一个数组中,这个数组就是hashmap的主干,当调用put方法时,会先计算entry的hashcode再通过与运算的方式计算出该entry应该存放的位置,当有位置重叠时,则通过头插法的方法组成链表;在1.8之后超过一定长度后,会进行扩容。
hashmap的默认长度为16,且初始化或扩展都必须是2的幂次数,这样可以减少碰撞,让数据分配更加均匀
4、hashmap的扩容问题
1)当数组长度超过阙值时,则需要进行数组扩容
2)当链表长度超过8个且数组的大小未超过64时,则扩容数组
3)当链表长度超过8个且数组的大小超过64时,则将链表转成红黑树
特别说明:高并发下扩容带来的问题
在多线程下,进行put操作可能会导致hashmap死循环,原因在于resize()方法,由于扩容是新建一个数组,复制原数据到数组。由于数组下标挂有链表,所以需要复制链表,但是多线程操作有可能导致环形链表。具体过程如下:
当A线程需要扩容时,此时B线程介入,原来A线程某个位置的链条是A-B-NULL,B线程扩容后变成B-A-NULL,由此就形成了死循环(jdk1.8已经解决了死循环问题)
5、HashMap和Hashtable的区别
1)结构层面:1.7之前,hashmap、hashtable底层都是数组+链表;1.8之后,当超过一定长度后,hashmap会将链表转成红黑树,hashtable没有这种机制
2)存值层面:hashmap允许key跟value都为null值,而hashtable都不能为null值
3)扩容层面:创建时若不指定容量初始值,hashmap默认初始化长度为16,之后每次扩容,容量都变为原来的2倍;hashtable默认值为11,每次扩容时,容量变为原来的2n+1倍
4)安全层面:hashmap线程不安全,hashtable线程安全
5)效率层面:hashmap效率高,hashtable效率低
6、hashmap和hashset的区别
1)接口层面:hashmap实现map接口,hashset实现set接口
2)结构层面:1.7之前,hashmap、hashset底层都是数组+链表;1.8之后,当超过一定长度后,hashmap会将链表转成红黑树,hashset没有这种机制
3)存值层面:hashmap存储键值对,hashset存储对象
4)安全层面:两者都不安全
5)效率层面:hashmap比hashset效率高
7、ConcurrentHashMap和hashtable 的区别
1)结构层面:JDK1.7的concurrenthashmap底层使用的是数组+链表;1.8之后,当超过一定长度后,concurrenthashmap会将链表转成红黑树,hashtable没有这种机制
2)安全层面:JDK1.7,concurrenthashmap对整个数组进行分割分段(segment),采用的是分段锁,一段数据配一把锁,多线程访问容器里不同数据端的数据,就不会存在锁竞争,进而提高并发访问率;到了1.8之后,取消了分段锁,采用CAS和synchronized来保证并发安全,数据结构跟hashmap1.8类似;而hashtable是使用synchronized来保证线程安全的
3)效率层面:concurrenthashmap的synchronized只锁定当前链表或红黑二叉树的首节点,只要hash不冲突,就不会产生并发,因此效率比hashtable高
相关文章: