集合的基本概念和区别

Collection

List : List中数据可重复
arrayList:底层为数组结构,默认初始化长度为10,扩容规则为1.5倍,第一次扩容为15
查询速度快,修改删除效率低。是线程不安全的。
linkList:底层为链表结构,添加删除速度快,查询速度慢。也是线程不安全的
vector(线程安全):底层为数组,扩容加载为1,第一次扩容长度为20,Vector线程安全,但是效率低,在方法上加了synchronize同步锁,保证了线程安全。

Set(接口) : 数据不能重复
实现类:
Hashset : 底层是hashmap,hashset中的值为key value默认为object对象,需要从写hashcode()和equals方法保证集合中元素不重复,相同的对象的hashCode值相等,hashCode相等但是可能不是同一对象,所以需要重写equals方法,如果是同一对象,则不添加,如果不是同一对象,发生哈希冲突,使用链地址法添加。
TreeSet:底层采用红黑树的形式实现,元素唯一且有序,内部实现了排序
LinkedHashSet:底层采用了链表和hashMap,线程不安全,效率高,链表保证了元素的有序性,hashMap保证了元素的唯一性

ArrayList与LinkedList的区别和适用场景
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。

LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。

Map

Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
HashMap : 线程不安全,效率比hashTable高,默认初始化容量为16,加载因子为0.75 ,以2倍进行扩容,JDK1.7的时候使用的是数组+ 单链表的数据结构。但是在JDK1.8及之后时,使用的是数组+链表+红黑树的数据结构(当链表的深度达到8的时候,也就是默认阈值,就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(n)变成O(logN)提高了效率),扩容采用2倍扩容因为如果只有2的n次幂的情况时最后一位二进制数才一定是1,这样能最大程度减少hash碰撞(hash值 & length-1),
key-value中key可以为null,但是只能有一个为null,value也可以为null
HashTable:线程安全,效率低,key-value不允许为空

解决哈希冲突:
开放定址法 1、 线性探测法
2、二次探测法
3、再哈希法
链地址法

为什么在JDK1.7的时候是先进行扩容后进行插入,而在JDK1.8的时候则是先插入后进行扩容的呢?

在jdk1.8后该操作相当于对HashMap的优化,当插入后,发现发生hash冲突则扩容,没有发生hash冲突则不扩容,在1.8之前,扩容是先达到阈值时,当下一个数据需要插入,则直接扩容,但是当插入的数据没有发生hash冲突则也会进行扩容操作,会产生无效扩容,所有1.8之后使用先插入后扩容可以减少一次无用的扩容,减少内存的使用

为什么在JDK1.8中进行对HashMap优化的时候,把链表转化为红黑树的阈值是8,而不是7或者不是20呢(面试蘑菇街问过)?

如果选择6和8(如果链表小于等于6树还原转为链表,大于等于8转为树),中间有个差值7可以有效防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。
还有一点重要的就是由于treenodes的大小大约是常规节点的两倍,因此我们仅在容器包含足够的节点以保证使用时才使用它们,当它们变得太小(由于移除或调整大小)时,它们会被转换回普通的node节点,容器中节点分布在hash桶中的频率遵循泊松分布,桶的长度超过8的概率非常非常小。所以作者应该是根据概率统计而选择了8作为阀值

你可能感兴趣的:(集合的基本概念和区别)