集合框架:

Collection集合详细介绍_集合框架

一、Set:

1、TreeSet:

基于红黑树实现,TreeSet是SortedSet接口的实现类,可以实现对集合进行自然排序(升序排序),可以保证元素处于排序状态。

2、HashSet:

基于哈希表实现,有很好的存取和查找性能,基本操作都是O(1)的时间复杂度,但是不支持有序性操作,不是线程同步的。

存储原理:

当想HashSet集合中存储一个元素时,HashSet会调用该对象的hashCode()方法调用其hashCode值,然后根据hashCode值决定该对象的存储位置。

查找原理:

在查找元素时,HashSet先计算元素的HashCode值,然后直接到hashCode值对应的位置去取出元素即可,这就是HashSet查询快的原因。

判断两个元素是否相等:

两个对象的equals()方法比较返回true和hashCode()方法返回值相等,则两个对象相等。

如果两个对象的equals相等,那么这两个对象的hashCode一定相等。

如果两个对象的hashCode相等,那么这两个对象的equals不一定相等。

3、LinkedHashSet:

同样是根据元素的hashCode值来决定元素的存储位置,同时使用链表来维护元素的次序。由于LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,但是在迭代访问Set里的全部元素时有很好的性能,特点是即拥有了O(1)的时间复杂度,又能够保留插入的顺序。

二、List:

1、ArrayList:

基于动态数组实现,支持随机访问。O(1),数组因为物理上的连续性,当要增加或删除元素时,在尾部还好,在其他地方就会导致后面的所有元素都要移动,所以效率较低。

2、LinkedList:

基于双向链表实现,只能顺序访问,但是可以快速的在链表中插入或删除元素。此外,LinkedList还可以用作栈、队列和双向队列。

ArrayList和LinkedList的区别:

① ArrayList底层使用了动态数组实现,实质上是一个动态数据

② LinkedList底层使用了双向链表实现,可以当做栈、队列、双向队列使用

③ ArrayList由于物理上的连续性,在随机存储(查找)方面效率高于LinkedList

④ LinkedList在节点的增删方面效率高于ArrayList

⑤ ArrayList必须预留一定的空间,当空间不足时,会进行扩容操作

⑥ LinkedList的开销是必须存储节点的信息以及节点的指针信息

三、Map:

Collection集合详细介绍_map_02

Map是一种键-值对(key-value)集合,Map集合中的每一个元素都包含一个键(key)对象和一个值(value)对象,提供了key到value的映射。Map中的可以key不允许重复,value允许重复。

1、TreeMap:

基于红黑树实现,每个键值对都是红黑树的一个节点。TreeMap是SortedMap的实现类,TreeMap存储key-value对时,需要根据key对节点进行排序。不是线程同步的。

2、HashTable:

基于哈希表实现,不允许空值作为键和值,线程安全的(基本不用了)

3、HashMap:

基于哈希表实现,当使用put()存储对象时,它会调用键对象的hashCode()方法来计算hashCode值,然后找到在数组中存放的索引,如果有冲突,则使用链表将所有哈希地址相同的元素串起来。查找对象时也是通过hashCode值查找的。

4、LinkedHashMap:

使用双向链表来维护元素的顺序,该链表负责维护Map的迭代顺序与插入顺序一致,因此性能比HashMap低。

HashMap和HashTable的区别:

1、HashMap的初始容量为16,扩容后为32,HashTable初始容量为11,扩容后为11×2+1=23

2、HashMap没有考虑同步,是线程不安全的,HashTable使用了synchronized关键字,是线程安全的

3、HashMap允许null作为key和value,HashTable不允许null作为key和value

4、HashMap线程不安全主要是考虑到了多线程环境下进行扩容可能会出现HashMap死循环

5、HashTable线程安全是由于在put和remove方法内部使用了synchronized进行了同步,所以对单个方法的使用是线程安全的,但是对多个方法进行复合操作时,线程安全无法保证。

HashMap实现原理:

JDK8及以后的版本中使用了数组+链表+红黑树,解决了链表太长导致查询速度变慢问题

HashMap是基于数据+链表实现的。HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap时,就会初始化一个数组。当往HashMap中put元素时,先根据key的hashCode重新计算hash值,然后根绝hash值得到这个元素在数组中的位置(即下标),如果数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式进行存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到该位置上。

       当系统决定存储HashMap中的key-value对时,完全没有考虑Entry中的value,仅仅根据key来计算并决定每个Entry的存储位置,可以把Map集合中的value当成key的附属,当系统决定了key的存储位置后,value的位置也就固定了。

       当从HashMap中get元素时,首先计算key的hashCode值,找到数组中对应的位置,然后通过key的equals方法在对应位置的链表中找到需要的元素。

       简单来说,HashMap在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。HashMap底层采用一个Entry[ ]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据hash算法来决定其在数组中的存储位置,再根据equals方法决定其在链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。

HashMap中的resize:

当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。为了提高查询效率,就要对HashMap的数组进行扩容,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去。

       当HashMap中元素的个数超过 数组大小×loadFactor 时,就会进行数组扩容,loadFactor的默认值是0.75,也就是说,默认情况下,数组大小为16,那么当元素个数超过12个的时候,就把数组的大小扩展为32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是非常消耗性能的

HashMap的几个构造器:

① HashMap():构造一个初始容量为16,负载因子为0.75的HashMap

② HashMap(int initialCapacity):构造一个初始容量为initialCapacity,负载因子为0.75的HashMap

③ HashMap(int initialCapacity, float loadFactor):以指定初始容量,指定负载因子创建一个HashMap。负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之越小。如果负载因子越大,对空间的利用就更充分,然而后果是查找效率降低,如果负载因子太小,散列表的数据将过于稀疏,使空间造成浪费。

ConcurrentHashMap和HashTable的区别:

① ConcurrentHashMap结合了HashMap和HashTable二者的优势。HashMap没有考虑同步,HashTable虽然考虑了同步的问题,但是HashTable在每次同步执行时都要锁住整个结构。

② ConcurrentHashMap锁的方式是稍微细粒度的,ConcurrentHashMap将hash表分为16个桶(默认值),get、put、remove等常用操作只锁上当前需要用到的桶

ConcurrentHashMap的具体实现方式(分段锁):

① 该类包含两个静态内部类MapEntry和Segment,MapEntry用来封装映射表的键值对,Segment用来充当锁的角色。

② 每个Segment守护一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment锁。

③ 实际开发中,在单线程环境下可以使用HashMap,多线程环境下可以使用ConcurrentHashMap

TreeMap有哪些特性:

① TreeMap底层使用红黑树实现,TreeMap中存储的键值对按照键来排序

② 如果key存入的是字符串等类型,那么会按照字典默认顺序排序

③ 如果传入的是自定义引用类型,比如说User,那么该对象必须实现Comparable接口,并且重写其compareTo方法;或者在创建TreeMap的时候,我们必须指定使用的比较器

Comparable接口和Comparator接口的区别:

① Comparable实现比较简单,但是当需要重新定义比较规则时,必须重写其中的compareTo方法

② Comparator接口不需要修改源代码,只需要在创建TreeMap时重新传入一个具有指定规则的比较器即可

equals方法和hashCode方法的关系:

要想判断的两个对象是否相等,需要先用hashCode方法计算对象在哈希表中的位置,如果两个对象的hashCode相等,就表示这两个对象在散列存储结构中处于同一位置,然后再判断equals方法,如果equals方法也相等,就表示这两个对象是同一个。

如果两个对象的equals相等,那么这两个对象的hashCode一定相等

如果两个对象的hashCode相等,不代表这两个对象就相同,只能说明这两个对象在散列存储结果中处于同一位置。

Collection和Collections的区别:

Collection是单列集合的顶层接口,Map是双列集合的顶层接口

Collections是一个集合的工具类,提供了排序,查找等操作集合的一些常用方法

Iterator是什么:

Iterator是迭代器,可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。

使用:① 调用一个集合对象的Iterator()方法来获取迭代器的实现类对象

          ② 使用next()方法获得下一个元素,第一次调用next()方法时,返回序列的第一个元素

          ③ 使用hasNext()方法判断是否还有下一个元素

          ④ 使用remove()方法删除元素

ListIterator与Iterator的区别:

Iterator可用来遍历Set和List集合,ListIterator只能用来遍历List集合

Iterator对集合只能正向遍历,ListIterator既可以正向遍历也可以反向遍历

ListIterator实现了Iterator接口,并包含其他功能,比如:增加元素、替换元素、获取前一个和后一个元素的索引 等等