集合知识汇总

实际开发中,常常用到的 ArrayList、LinkedList、HashMap、LinkedHashMap 等集合类,其实涵盖了不少数据结构和算法,每一个类能够说都是精华,今天和你们一块儿来梳理一下!java

1、摘要

在 Java 中,集合大体能够分为两大致系,一个是 Collection,另外一个是 Map,都位于java.util包下。程序员

  • Collection :主要由 List、Set、Queue 接口组成,List 表明有序、重复的集合;其中 Set 表明无序、不可重复的集合;Java 5 又增长了 Queue 体系集合,表明队列集合。
  • Map:则表明具备映射关系的键值对集合。

不少书籍将 List、Set、Queue、Map 等能存放元素的接口体系,也概括为容器,由于他们能够存放元素!面试

集合和容器,这二者只是在概念上定义不一样,好比ArrayList是一个存放数组的对象,真正用起来并不会去关心这个东西究竟是集合仍是容器,把东西用好才是关键!算法

java.util.Collection下的接口和继承类关系简易结构图:
file
https://imgconvert.csdnimg.cn...segmentfault

java.util.Map下的接口和继承类关系简易结构图:
file
https://imgconvert.csdnimg.cn...数组

须要注意的是,网上有些集合架构图将 Map 画成继承自 Collection,实际上 Map 并无继承 Collection,Map 是单独的一个集合体系,这点须要纠正一下。
file
https://imgconvert.csdnimg.cn...缓存

在 Java 集合框架中,数据结构和算法能够说在里面体现的淋淋尽致,这一点能够从咱们以前对各个集合类的分析就能够看的出来,如动态数组、链表、红黑树、Set、Map、队列、栈、堆等,基本上只要出去面试,集合框架的话题必定不会少!安全

下面咱们就一块儿来看看各大数据结构的具体实现!数据结构

2、List

List 集合的特色:存取有序,能够存储重复的元素,能够用下标进行元素的操做。多线程

List 集合中,各个类继承结构图:
file
https://imgconvert.csdnimg.cn...

从图中能够看出,List 主要实现类有:ArrayList、LinkedList、Vector、Stack。

2.一、ArrayList

ArrayList 是一个动态数组结构,支持随机存取,在指定的位置插入、删除效率低(由于要移动数组元素);若是内部数组容量不足则自动扩容,扩容系数为原来的1.5倍,所以当数组很大时,效率较低。

file
https://imgconvert.csdnimg.cn...

固然,插入删除也不是效率很是低,在某些场景下,好比尾部插入、删除,由于不须要移动数组元素,因此效率也很高哦!

ArrayList 是一个非线程安全的类,在多线程环境下使用迭代器遍历元素时,会报错,抛ConcurrentModificationException异常!

所以,若是想要在多线程环境下使用 ArrayList,建议直接使用并发包中的CopyOnWriteArrayList!

2.二、LinkedList

LinkedList 是一个双向链表结构,在任意位置插入、删除都很方便,可是不支持随机取值,每次都只能从一端开始遍历,直到找到查询的对象,而后返回;不过,它不像 ArrayList 那样须要进行内存拷贝,所以相对来讲效率较高,可是由于存在额外的前驱和后继节点指针,所以占用的内存比 ArrayList 多一些。
file
https://imgconvert.csdnimg.cn...

LinkedList 底层经过双向链表实现,经过first和last引用分别指向链表的第一个和最后一个元素,注意这里没有所谓的哑元(某个参数若是在子程序或函数中没有用到,那就被称为哑元),当链表为空的时候first和last都指向null。

2.三、Vector

Vector 也是一个动态数组结构,一个元老级别的类,早在 jdk1.1 就引入进来了,以后在 jdk1.2 里引进 ArrayList,ArrayList 能够说是 Vector 的一个迷你版,ArrayList 大部分的方法和 Vector 比较类似!

二者不一样的是,Vector 中的方法都加了synchronized,保证操做是线程安全的,可是效率低,而 ArrayList 全部的操做都是非线程安全的,执行效率高,但不安全!

对于 Vector,虽然能够在多线程环境下使用,可是在迭代遍历元素的时候依然会报错,抛ConcurrentModificationException异常!

在 JDK 中 Vector 已经属于过期的类,官方不建议在程序中采用,若是想要在多线程环境下使用 Vector,建议直接使用并发包中的CopyOnWriteArrayList!

2.四、Stack

Stack 是 Vector 的一个子类,本质也是一个动态数组结构,不一样的是,它的数据结构是先进后出,取名叫栈!

不过,关于 Java 中 Stack 类,有不少的质疑声,栈更适合用队列结构来实现,这使得 Stack 在设计上不严谨,所以,官方推荐使用 Deque 下的类来是实现栈!

2.五、小结

List 接口各个实现类性能比较,如图:
file
https://imgconvert.csdnimg.cn...

  • ArrayList(动态数组结构),查询快(随意访问或顺序访问),增删慢,但在末尾插入删除,速度与LinkedList相差无几,可是是非线程安全的!
  • LinkedList(双向链表结构),查询慢,增删快,也是非线程安全的!
  • Vector(动态数组结构),由于方法加了同步锁,相比 ArrayList 执行都慢,基本不在使用,若是须要在多线程下使用,推荐使用并发容器中的CopyOnWriteArrayList来操做,效率高!
  • Stack(栈结构)继承于Vector,数据是先进后出,基本不在使用,若是要实现栈,推荐使用 Deque 下的 ArrayDeque,效率比 Stack 高!

3、Map

Map是一个双列集合,其中保存的是键值对,键要求保持惟一性,值能够重复。

从上文中,咱们了解到 Map 集合结构体系以下:
file
https://imgconvert.csdnimg.cn...

从图中能够看出,Map 的实现类有 HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties 等等。

3.一、HashMap

关于 HashMap,相信你们都不陌生,是一个使用很是频繁的容器类,继承自 AbstractMap,它容许键值都放入 null 元素,可是 key 不可重复。

由于使用的是哈希表存储元素,因此输入的数据与输出的数据,顺序基本不一致,另外,HashMap 最多只容许一条记录的 key 为 null。

在 jdk1.7中,HashMap 主要是由 数组+ 单向链表 组成,当发生 hash 冲突的时候,就将冲突的元素放入链表中。
file
https://imgconvert.csdnimg.cn...

从 jdk1.8开始,HashMap 主要是由 数组+单向链表+红黑树 实现,相比 jdk1.7 而言,多了一个红黑树实现。

当链表长度超过 8 的时候,就将链表变成红黑树,如图所示。
file

HashMap 的实现原理,算是面试必问的一个话题,详细的实现过程,有兴趣的朋友能够阅读小编以前写的文章《深刻分析HashMap》

HashMap 虽然很强大,可是它是非线程安全的,也就是说,若是在多线程环境下使用,可能由于程序自动扩容操做将单向链表转变成了循环链表,在查询遍历元素的时候,形成程序死循环!此时 CPU 直接会飙到 100%!

若是咱们想在多线程环境下使用 HashMap,其中一个推荐的解决办法就是使用 java 并发包下的 ConcurrentHashMap 类!

在 JDK1.7 中,ConcurrentHashMap 类采用了分段锁的思想,将 HashMap 进行切割,把 HashMap 中的哈希数组切分红小数组(Segment),每一个小数组有 n 个 HashEntry 组成,其中 Segment 继承自ReentrantLock(可重入锁),从而实现并发控制!

file
https://imgconvert.csdnimg.cn...

从 jdk1.8 开始,ConcurrentHashMap 类取消了 Segment 分段锁,采用 CAS + synchronized来保证并发安全,数据结构跟 jdk1.8 中 HashMap 结构保持一致,都是 数组 + 链表(当链表长度大于8时,链表结构转为红黑树)结构。
在这里插入图片描述
https://imgconvert.csdnimg.cn...

jdk1.8 中的 ConcurrentHashMap 中 synchronized 只锁定当前链表或红黑树的首节点,只要节点 hash 不冲突,就不会产生并发,相比 JDK1.7 的 ConcurrentHashMap 效率又提高了 N 倍!

详细的实现原理,能够参阅小编以前写的文章《深刻分析ConcurrentHashMap》

3.二、LinkedHashMap

LinkedHashMap 继承自 HashMap,能够认为是 HashMap + LinkedList,它既使用 HashMap 操做数据结构,又使用 LinkedList 维护插入元素的前后顺序,内部采用双向链表(doubly-linked list)的形式将全部元素( entry )链接起来。

从名字上,就能够看出 LinkedHashMap 是一个 LinkedList 和 HashMap 的混合体,同时知足 HashMap 和 LinkedList 的某些特性,可将 LinkedHashMap 看做采用 Linkedlist 加强的HashMap。
file
https://imgconvert.csdnimg.cn...

双向链表头部插入的数据为链表的入口,迭代器遍历方向是从链表的头部开始到链表尾部结束,结构图以下:
file
https://imgconvert.csdnimg.cn...

这种结构除了能够保持迭代的顺序,还有一个好处:迭代 LinkedHashMap 时不须要像 HashMap 那样遍历整个 table,而只须要直接遍历 header 指向的双向链表便可,也就是说 LinkedHashMap 的迭代时间就只跟 entry 的个数相关,而跟 table 的大小无关。

LinkedHashMap 继承了 HashMap,全部大部分功能特性与 HashMap 基本相同,容许放入 key 为null的元素,也容许插入 value 为null的元素。

两者惟一的区别是 LinkedHashMap 在 HashMap 的基础上,采用双向链表(doubly-linked list)的形式将全部 entry 链接起来,这样是为保证元素的迭代顺序跟插入顺序相同。

3.三、TreeMap

TreeMap实现了 SortedMap 接口,也就是说会按照 key 的大小顺序对 Map 中的元素进行排序,key 大小的评判能够经过其自己的天然顺序(natural ordering),也能够经过构造时传入的比较器(Comparator)。

TreeMap 底层经过红黑树(Red-Black tree)实现,因此要了解 TreeMap 就必须对红黑树有必定的了解。

以下为红黑树图:
file
https://imgconvert.csdnimg.cn...

红黑树是基于平衡二叉树实现的,基于平衡二叉树的实现还有 AVL 算法,也被称为 AVL 树,AVL 算法要求任何节点的两棵子树的高度差不大于1,为了保持树的高度差不大于1,主要有2中调整方式:左旋转、右旋转。

绕某元素右旋转,图解以下:
file
https://imgconvert.csdnimg.cn...

绕某元素右旋转,图解以下:
file
https://imgconvert.csdnimg.cn...

虽然 AVL 算法保证了树高度平衡,查询效率极高,可是也有缺陷,在删除某个节点时,须要屡次左旋转或右旋转调整,红黑树的出现就是为了解决尽量少的调整,提升平衡二叉树的总体性能!

那么对于一棵有效的红黑树,主要有如下规则:

一、每一个节点要么是红色,要么是黑色,但根节点永远是黑色的;

二、每一个红色节点的两个子节点必定都是黑色;

三、红色节点不能连续(也便是,红色节点的孩子和父亲都不能是红色);

四、从任一节点到其子树中每一个叶子节点的路径都包含相同数量的黑色节点;

五、全部的叶节点都是是黑色的(注意这里说叶子节点实际上是上图中的 NIL 节点);

红黑树为了保证树的基本平衡,调整也有相似 AVL 算法那样的左旋转、右旋转操做;同时,红黑树由于红色节点不能连续,所以还增长一个颜色调整操做:节点颜色转换。
file
https://imgconvert.csdnimg.cn...

AVL虽然能够保证平衡二叉树高度平衡,可是在删除某个节点的时候,最多须要 n 次调整,也就是左旋转、右旋转;而红黑树,虽然也是基于平衡二叉树实现,可是并不像 AVL 那样高度平衡,而是基本平衡,在删除某个节点的时候,至多 3 次调整,效率比 AVL 高!

红黑树,在插入的时候,与 AVL 同样,结点最多只须要2次旋转;在查询方面,由于不是高度平衡,红黑树在查询效率方面稍逊于 AVL,可是比二叉查找树强不少!

就总体来看,红黑树的性能要好于 AVL,只是实现稍微复杂了一些,有兴趣的朋友能够参阅小编以前写的文章《3分钟看完关于树的故事》

3.四、IdentityHashMap

IdentityHashMap 从名字上看,感受表示惟一的 HashMap,而后并非,别被它的名称所欺骗哦。

IdentityHashMap 的数据结构很简单,底层实际就是一个 Object 数组,在存储上并无使用链表来存储,而是将 K 和 V 都存放在 Object 数组上。
file
https://imgconvert.csdnimg.cn...

当添加元素的时候,会根据 Key 计算获得散列位置,若是发现该位置上已经有改元素,直接进行新值替换;若是没有,直接进行存放。当元素个数达到必定阈值时,Object 数组会自动进行扩容处理。

IdentityHashMap 的实现也不一样于 HashMap,虽然也是数组,不过 IdentityHashMap 中没有用到链表,解决冲突的方式是计算下一个有效索引,而且将数据 key 和 value 紧挨着存入 map 中,即table[i]=key、table[i+1]=value;

IdentityHashMap 容许key、value都为null,当key为null的时候,默认会初始化一个Object对象做为key;

3.五、WeakHashMap

WeakHashMap 是 Map 体系中一个很特殊的成员,它的特殊之处在于 WeakHashMap 里的元素可能会被 GC 自动删除,即便程序员没有显示调用 remove() 或者 clear() 方法。

换言之,当向 WeakHashMap 中添加元素的时候,再次遍历获取元素,可能发现它已经不见了,咱们来看看下面这个例子。

public static void main(String[] args) {
        Map weakHashMap = new WeakHashMap();
        //向weakHashMap中添加3个元素
        weakHashMap.put("key-1", "value-1");
        weakHashMap.put("key-2", "value-2");
        weakHashMap.put("key-3", "value-3");
        //输出添加的元素
        System.out.println("数组长度:"+weakHashMap.size() + ",输出结果:" + weakHashMap);
        //主动触发一次GC
        System.gc();
        //再输出添加的元素
        System.out.println("数组长度:"+weakHashMap.size() + ",输出结果:" + weakHashMap);
}

输出结果:
数组长度:3,输出结果:{key-2=value-2, key-1=value-1, key-0=value-0}
数组长度:3,输出结果:{}
当主动调用 GC 回收器的时候,再次查询 WeakHashMap 里面的数据的时候,数据已经被 GC 收集了,内容为空。

这是由于 WeakHashMap 的 key 使用了弱引用类型,在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。

不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。

WeakHashMap 跟普通的 HashMap 不一样,在存储数据时,key 被设置为弱引用类型,而弱引用类型在 java 中,可能随时被 jvm 的 gc 回收,因此再次经过获取对象时,可能获得空值,而 value 是在访问数组内容的时候,进行清除。

可能不少人以为这样作很奇葩,其实否则,WeekHashMap 的这个特色特别适用于须要缓存的场景。

在缓存场景下,因为系统内存是有限的,不能缓存全部对象,可使用 WeekHashMap 进行缓存对象,即便缓存丢失,也能够经过从新计算获得,不会形成系统错误。

比较典型的例子,Tomcat 中的 ConcurrentCache 类就使用了 WeekHashMap 来实现数据缓存。

3.六、Hashtable

Hashtable 一个元老级的集合类,早在 JDK 1.0 就诞生了,而 HashMap 诞生于 JDK 1.2。

在实现上,HashMap 能够看做是 Hashtable 的一个迷你版,虽然两者的底层数据结构都是 数组 + 链表 结构,具备查询、插入、删除快的特色,可是两者又有不少的不一样。

  • 虽然 HashMap 和 Hashtable 都实现了 Map 接口,但 Hashtable 继承于 Dictionary 类,而 HashMap 是继承于 AbstractMap;
  • HashMap 能够容许存在一个为 null 的 key 和任意个为 null 的 value,可是 HashTable 中的 key 和 value 都不容许为 null;
  • Hashtable 的方法是同步的,由于在方法上加了 synchronized 同步锁,而 HashMap 是非线程安全的;

虽然 Hashtable 是 HashMap 线程安全的一个版本,可是官方已经不推荐使用 HashTable了,由于历史遗留缘由,并无移除此类,若是须要在线程安全的环境下使用 HashMap,那么推荐使用 ConcurrentHashMap。在上文中已经有所提到!

3.七、Properties

在 Java 中,其实还有一个很是重要的类 Properties,它继承自 Hashtable,主要用于读取配置文件。

Properties 类是 java 工具包中很是重要的一个类,好比在实际开发中,有些变量,咱们能够直接硬写入到自定义的 java 枚举类中。

可是有些变量,在测试环境、预生产环境、生产环境,变量所须要取的值都不同,这个时候,咱们能够经过使用 properties 文件来加载程序须要的配置信息,以达到一行代码,多处环境均可以运行的效果!

好比,最多见的 JDBC 数据源配置文件,properties文件以.properties做为后缀,文件内容以键=值格式书写,左边是变量名称,右边是变量值,用#作注释,新建一个jdbc.properties文件,内容以下:
file

Properties 继承自 Hashtable,大部分方法都复用于 Hashtable,与 Hashtable 不一样的是, Properties 中的 key 和 value 都是字符串。

实际开发中,Properties 主要用于读取配置文件,尤为是在不一样的环境下,变量值须要不同的状况,能够经过读取配置文件来避免将变量值写死在 java 的枚举类中,以达到一行代码,多处运行的目的!

4、Set

Set集合的特色:元素不重复,存取无序,无下标。

Set 集合,在元素存储方面,注重独立无二的特性,若是某个元素在集合中已经存在,不会存储重复的元素,同时,集合存储的是元素,不像 Map 集合那样存储的是键值对。

Set 集合中,各个类继承结构图:
file
https://imgconvert.csdnimg.cn...

从图中能够看出,Set 集合主要实现类有: HashSet、LinkedHashSet 、TreeSet 、EnumSet( RegularEnumSet、JumboEnumSet )等等

4.一、HashSet

HashSet 是一个输入输出无序的集合,底层基于 HashMap 来实现,HashSet 利用 HashMap 中的 key 元素来存放元素,这一点咱们能够从源码上看出来,源码定义以下:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable{
    // HashMap 变量
    private transient HashMap<E,Object> map;
    /**HashSet 初始化*/
    public HashSet() {
        //默认实例化一个 HashMap
        map = new HashMap<>();
    }
}

由于 HashMap 容许 key 为空为null,因此 HashSet 也容许添加为null的元素。

4.二、LinkedHashSet

LinkedHashSet 是一个输入输出有序的集合,继承自 HashSet,可是底层基于 LinkedHashMap 来实现。

LinkedHashSet 的源码,类定义以下:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    public LinkedHashSet() {
        //调用 HashSet 的方法
        super(16, .75f, true);
    }
}

查询源码,super调用的方法,源码以下:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        //初始化一个 LinkedHashMap
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

LinkedHashSet 与 HashSet 相比,LinkedHashSet 保证了元素输入输出有序!

4.三、TreeSet

TreeSet 是一个排序的集合,实现了NavigableSet、SortedSet、Set接口,底层基于 TreeMap 来实现。

TreeSet 利用 TreeMap 中的 key 元素来存放元素,这一点咱们也能够从源码上看出来,阅读源码,类定义以下:

public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
    //TreeSet 使用NavigableMap接口做为变量
    private transient NavigableMap<E,Object> m;
    /**对象初始化*/
    public TreeSet() {
        //默认实例化一个 TreeMap 对象
        this(new TreeMap<E,Object>());
    }
    //对象初始化调用的方法
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
}

new TreeSet<>()对象实例化的时候,表达的意思,能够简化为以下:

NavigableMap<E,Object> m = new TreeMap<E,Object>();

由于TreeMap实现了NavigableMap接口,因此没啥问题。

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable{
    ......
}

TreeSet 是一个排序的 Set 集合,元素不可重复,底层基于 TreeMap 的 key 来实现,元素不能够为空,默认按照天然排序来存放元素,也可使用 Comparable和 Comparator 接口来比较大小,实现自定义排序。

4.四、EnumSet

EnumSet 是一个与枚举类型搭配使用的专用 Set 集合,在 jdk1.5 中加入。

与 HashSet、LinkedHashSet 、TreeSet 不一样的是,EnumSet 元素必须是Enum的类型,而且全部元素都必须来自同一个枚举类型。

新建一个EnumEntity的枚举类型,定义2个参数。

public enum EnumEntity {
    WOMAN,MAN;
}

建立一个 EnumSet,并将枚举类型的元素所有添加进去!

//建立一个 EnumSet,将EnumEntity 元素内容添加到EnumSet中
EnumSet<EnumEntity> allSet = EnumSet.allOf(EnumEntity.class);
System.out.println(allSet);

输出结果:
[WOMAN, MAN]

在 Java 中,EnumSet 是一个虚类,有2个实现类 RegularEnumSet、JumboEnumSet,不能显式的实例化改类,EnumSet 会动态决定使用哪个实现类,当元素个数小于等于64的时候,使用 RegularEnumSet;大于 64的时候,使用JumboEnumSet类。

EnumSet 其内部使用位向量实现,拥有极高的时间和空间性能,若是元素是枚举类型,推荐使用 EnumSet。

4.五、小结

file
https://imgconvert.csdnimg.cn...

5、Queue

在 jdk1.5 中,新增了 Queue 接口,表明一种队列集合的实现。

Queue 接口是由大名鼎鼎的 Doug Lea 建立,中文名为道格·利,翻开 JDK1.8 源代码,能够将 Queue 接口旗下的实现类抽象成以下结构图:

file
https://imgconvert.csdnimg.cn...

从上图能够看出,Queue 接口主要实现类有:ArrayDeque、LinkedList、PriorityQueue。

5.一、ArrayDeque

ArrayQueue 是一个基于数组实现的双端队列,能够想象,在队列中存在两个指针,一个指向头部,一个指向尾部,所以它具备FIFO队列及LIFO栈的方法特性。

其中队列(FIFO)表示先进先出,好比水管,先进去的水先出来;栈(LIFO)表示先进后出,好比,手枪弹夹,最后进去的子弹,最早出来。

以下为 ArrayDeque 数据结构图,head 表示头部指针,tail表示尾部指针:
file
https://imgconvert.csdnimg.cn...

在上文中,咱们也说到 Stack 也能够做为栈使用,可是 ArrayDeque 的效率要高于 Stack 类,而且功能也比 Stack 类丰富的多,当须要使用栈时,Java 已不推荐使用 Stack,而是推荐使用更高效的 ArrayDeque。

5.二、LinkedList

LinkedList 是一个基于链表实现的双端队列,在上文中咱们也说到 LinkedList 实现自 List,做为一个双向链表时,增长或删除元素的效率较高,若是查找的话须要遍历整个链表,效率较低!
file
https://imgconvert.csdnimg.cn...

于此同时,LinkedList 也实现了 Deque 接口,既能够做队列使用也能够做为栈使用。

从上图中能够得出,ArrayDeque 和 LinkedList 都是 Deque 接口的实现类,都具有既能够做为队列,又能够做为栈来使用的特性,二者主要区别在于底层数据结构的不一样。

ArrayDeque 底层数据结构是以循环数组为基础,而 LinkedList 底层数据结构是以循环链表为基础。

理论上,链表在添加、删除方面性能高于数组结构,在查询方面数组结构性能高于链表结构,可是对于数组结构,若是不进行数组移动,在添加方面效率也很高。

下面,分别以10万条数据为基础,经过添加、删除,来测试二者做为队列、栈使用时所消耗的时间,测试结果以下图:
file
https://imgconvert.csdnimg.cn...

从数据上能够看出,在 10 万条数据下,二者性能都差很少,当达到 100 万条、1000 万条数据的时候,二者的差异就比较明显了,ArrayDeque 不管是做为队列仍是做为栈使用,性能均高于 LinkedList 。

为何 ArrayDeque 性能,在大数据量的时候,明显高于 LinkedList?

我的分析,LinkedList 底层是以循环链表来实现的,每个节点都有一个前驱、后继的变量,也就是说,每一个节点上都存放有它上一个节点的指针和它下一个节点的指针,同时还包括它本身的元素,每次插入或删除节点时须要修改前驱、后继节点变量,在同等的数据量状况下,链表的内存开销要明显大于数组,同时由于 ArrayDeque 底层是数组结构,自然在查询方面占优点,在插入、删除方面,只须要移动一下头部或者尾部变量,时间复杂度是 O(1)。

因此,在大数据量的时候,LinkedList 的内存开销明显大于 ArrayDeque,在插入、删除方面,都要频发修改节点的前驱、后继变量;而 ArrayDeque 在插入、删除方面依然保存高性能。

若是对于小数据量,ArrayDeque 和 LinkedList 在效率方面相差不大,可是对于大数据量,推荐使用 ArrayDeque。

5.三、PriorityQueue

PriorityQueue 并无直接实现 Queue接口,而是经过继承 AbstractQueue 类来实现 Queue 接口一些方法,在 Java 定义中,PriorityQueue 是一个基于优先级的无界优先队列。

通俗的说,添加到 PriorityQueue 队列里面的元素都通过了排序处理,默认按照天然顺序,也能够经过 Comparator 接口进行自定义排序。

优先队列的做用是保证每次取出的元素都是队列中权值最小的。

PriorityQueue 排序实现与上文中说到的 TreeMap 相似。
file
https://imgconvert.csdnimg.cn...

在 Java 中 PriorityQueue 是一个使用数组结构来存储元素的优先队列,虽然它也实现了Queue接口,可是元素存取并非先进先出,而是经过一个二叉小顶堆实现的,默认底层使用天然排序规则给插入的元素进行排序,也可使用自定义比较器来实现排序,每次取出的元素都是队列中权值最小的。

同时须要注意的是,PriorityQueue 不能插入null,不然报空指针异常!

6、总结

以上主要是对 java 集合往期文章内容作了一个简单的总结,有兴趣的能够参阅各篇详细内容,若是有理解不当之处,欢迎指正!

转自:https://blog.csdn.net/javagee...

相关文章
相关标签/搜索