Java 集合复习

复习范围:


集合复习

ArrayList与LinkedList区别

  • 线程安全:两个都是不同步的,即不保证线程安全
  • 数据结构:ArrayList用的是数组实现,LinkedList用的是双向链表。
  • 插入删除,随机访问:ArrayList因为是数组,所以插入删除末尾元素的话时间复杂度只需O(1),如果是指定位置i的元素,因为要对i之后的元素进行往后一位或者往前一位的操作,时间复杂度为O(n-i)。LinkedList是链表,所以插入删除指定位置i的元素就要一直移动到指定位置再删除,时间复杂度为O(i),但是如果在末尾删除插入元素,时间复杂度为O(1)。ArrayList支持随机访问,直接通过数组下标就可以访问元素了。LinkedList则不支持,这也是数据结构的区别造成的。
  • 空间占用:ArrayList的结尾需要预留一定的容量空间,而LinkedList每个元素都要有直接前继,直接后继,元素大小。所以每个元素消耗的空间大于ArrayList。

ArrayList扩容机制

minCapacity:所需的最小容量
当ArrayList增加元素时,minCapacity就会+1,这个时候拿他和默认值10进行比较,取最大值赋给minCapacity。当minCapacity大于当前ArrayList的长度后,就会调用grow方法来进行扩容。

扩容过程:先把旧容量(当前数组的长度,不是数组元素的个数)变为之前的1.5倍,再和minCapacity相比,如果还是小于minCapacity就会把minCapacity的值赋给新容量(newCapacity)。如果新容量大于ArrayList所定义的最大容量MAX_ARRAY_SIZE,newCapacity就为Interger.MAX_VALUE,否则是MAX_ARRAY_SIZE。最后把原数组内容放到新的数组里面。

HashMap

JDK1.8以前是用的数组+链表的形式,,先对key的hashcode进行hash方法,得到一个hash值,然后用hash & ( n - 1 )来得到该存放在数组的哪一个位置,如果这时候数组该位置已经有一个了,那么判断两者的hash和key是不是相同,相同就直接覆盖了,不同的话就在数组的该位置生成一个链表,把后者加进链表里面。
这样子的话如果所有key都存放在数组的一个链表上面,那么查找效率就是从链表的第一个节点往下面找,是个线性结构,就会导致效率过低,所以JDK1.8引入了红黑树来辅助查找。当链表长度大于指定值(一般是8),这个时候就会判断数组长度是否大于64,大于的话链表就会转化为红黑树,小于的话会对数组进行扩容。

HashMap多线程的问题

多线程中,HashMap扩容后要重新计算hash值,也就是rehash后,元素之间会存在一个死循环链表,然后就会一直在运行,浪费资源。jdk1.8之后解决了这个问题。

HashMap和HashTable区别

  • 数据结构:HashMap用的是数组+链表的形式,如果链表长度大于8,数组长度大于64,链表就会转成红黑树。HashTable不会转红黑树。
  • 存放null :HashMap允许存放key和value都为null的值。key只允许存放一个,value可以存放多个。HashTable不允许存放null。
  • 线程安全:HashMap在多线程的时候是不安全的,HashTable是安全的,里面的方法经过synchronized修饰过。(注意:HashTable现在不推荐使用,如果要保证线程安全就要使用ConcurrentHashMap!)
  • 效率:HashMap效率会高一点,因为线程安全的问题。
  • 初始容量:HashMap默认值是16,如果要扩容就会乘2,因为他要保证长度永远是2的幂次方。如果你给了一个初始值,就会把它扩充成2的幂次方。HashTable默认值是11,每次扩容就是原来的2n+1。给定初始值的话,HashTable会直接使用你给的值。

HashMap和HashSet区别

HashSet基于HashMap实现,所以都是线程不安全

  • HashMap实现了Map接口,HashSet实现了set接口
  • HashMap存储了键值对,HashSet存储对象
  • HashMap使用key计算hashcode。HashSet使用成员对象来计算hashcode,对于两个对象可能hashcode相等,这个时候就要用equals来判断两个对象相不相等。
  • HashMap添加时遇到重复值是覆盖,HashSet遇到重复值是把后来的放弃掉,维持原来的。

HashSet检查重复

当你添加对象到HashSet中,HashSet计算对象的hashcode值,与其他对象的hashcode进行比较,如果有相同hashcode的对象,就调用equals方法来检查两个对象是不是相等,相等就不把对象添加进HashSet中。

LinkedHashMap

LinkedHashMap可以把添加的数据,按添加的顺序输出 。用的是双向链表。

ConcurrentHashMap线程安全

JDK1.7:把数据分成一段一段,分开上锁,如果一个线程对第一段数据进行操作,那么其他线程依旧可以访问其他段数据。由Segment数组结构和HashEntry数组结构组成。Segment负责锁的角色,HashEntry负责存储键值对。
一个ConcurrentHashMap有一个Segment数组,每个Segment有一个HashEntry数组。每个Segment都会守护一个HashEntry的数据,如果要进行操作,必须获得相对应的Segment。

JDK1.8:放弃了Segment,才用CAS和synchronized来保证安全。数据结构和HashMap一样,采用Node数组+链表/红黑树的结构。synchronized锁定当前链表和红黑树的首节点,只要hash不冲突,就不会产生并发。

ConcurrentHashMap与HashTable

HashTable使用Synchronized来保证线程安全,效率比较低。当一个线程进行put操作时,其他线程无法进行put和get操作。ConcurrentHashMap采用的是数据分成一段段进行上锁,这样子就不会产生过多的阻塞,也能保证线程安全。

根据资料:
本文主要根据JavaGuide的资料
ArrayList简介

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容