java中的具体容器类都不是从头构建的,他们都继承了一些抽象容器类。这些抽象容器类,提供了容器接口的部分实现,方便具体容器类在抽象类的基础上做具体实现。容器类和接口的关系架构图如下:

虚线框表示接口,有Collection, List, Set, Queue, Deque和Map。

有六个抽象容器类:

  • AbstractCollection: 实现了Collection接口,被抽象类AbstractList, AbstractSet, AbstractQueue继承,ArrayDeque也继承自AbstractCollection (图中未画出)。
  • AbstractList:父类是AbstractCollection,实现了List接口,被ArrayList, AbstractSequentialList继承。
  • AbstractSequentialList:父类是AbstractList,被LinkedList继承。
  • AbstractMap:实现了Map接口,被TreeMap, HashMap, EnumMap继承。
  • AbstractSet:父类是AbstractCollection,实现了Set接口,被HashSet, TreeSet和EnumSet继承。
  • AbstractQueue:父类是AbstractCollection,实现了Queue接口,被PriorityQueue继承。

这些抽象类都提供了相应接口的基础实现,同时定义了一些子类必须重写的抽象方法 ,相当于定义了一个模板,这有点类似于一个设计模式——模板方法模式

下面主要总结一下上图中具体实现类的一些细节和特性:

ArrayList

内部使用Object数组实现、默认初始大小是10,可手动指定,扩容策略是 newCapacity = oldCapacity + (oldCapacity >> 1),
即每次增大到原来的1.5倍,但不会小于最小值,最大容量是Integer.MAX_VALUE,即0x7fffffff。扩容时将一个新建一个数组将原来数组的内容复制过去。

没用泛型数组E[]原因个人猜测是因为java中的泛型是在jdk1.5才开始支持的,在这之前都用的Object,为了保持向前的兼容性不得不这么做,jdk1.6中新增的ArrayDeque内部就是泛型数组E[]。

LinkedList
内部使用链表实现,可以用作队列(Queue),队列两端都可以操作,尾部添加,头部查看和删除。也可以用作双端队列(Deque)(双向链表)(双向遍历、可以实现栈的功能)。

内部只维护头和尾结点,构造方法不能指定大小,按需分配空间。

性能

  • 不可以随机访问,必须从头或尾开始查找,效率为O(N/2)(用了二分法)
  • 按照内容查找效率为O(N/2)
  • 两端添加删除效率为O(1)
  • 中间插入删除先定位 ,效率为O(n),但插入或者删除本身效率很高,为O(1)

ArrayDeque
这是jdk1.6新增的一个类。内部维护一个泛型数组E[],使用head和tail两个int索引表示链表头和尾(其中tail指向下一个空位),使得物理上简单的从头到尾的数组变成了逻辑上循环的数组,可以作为链表使用。默认初始数组大小是16,扩容策略是乘以2

  • 如果head和tail相同,则数组为空,长度为0。
  • 如果tail大于head,则第一个元素为elements[head],最后一个为elements[tail-1],长度为tail-head,元素索引从head到tail-1。
  • 如果tail小于head,且为0,则第一个元素为elements[head],最后一个为elements[elements.length-1],元素索引从head到elements.length-1。
  • 如果tail小于head,且大于0,则会形成循环,第一个元素为elements[head],最后一个是elements[tail-1],元素索引从head到elements.length-1,然后再从0到tail-1。

可以通过构造方法指定初始数组大小numElements,但是实际的大小是:

  • 如果numElements小于MIN_INITIAL_CAPACITY,则分配的数组长度就是MIN_INITIAL_CAPACITY,它是一个静态常量,值为8。
  • 在numElements大于等于8的情况下,分配的实际长度是严格大于numElements并且为2的整数次幂的最小数。比如,如果numElements为10,则实际分配16,如果numElements为32,则为64。

使用2的幂次方是为了保证

public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}

(tail + 1) & (elements.length - 1)能定位到正确下一个位置tail,比如说,如果elements.length为8,则(elements.length - 1)为7,二进制为0111,对于负数-1,与7相与,结果为7,对于正数8,与7相与,结果为0,都能达到循环数组中找下一个正确位置的目的。

扩容之后会重新排列数组,head是0,tail是当前元素数。

性能

  • 在两端添加、删除元素的效率很高,动态扩展需要的内存分配以及数组拷贝开销可以被平摊,具体来说,添加N个元素的效率为O(N)。
  • 根据元素内容查找和删除的效率比较低,为O(N)。

简单总结:

与ArrayList和LinkedList不同,ArrayDeque没有索引位置的概念,不能根据索引位置进行操作。
ArrayDeque和LinkedList都实现了Deque接口,应该用哪一个呢?如果只需要Deque接口,从两端进行操作,一般而言,ArrayDeque效率更高一些,应该被优先使用,不过,如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除,则应该选LinkedList。

Java集合总结(一):列表和队列的更多相关文章

  1. Java集合框架之LinkedList-----用LinkedList模拟队列和堆栈

    LinkedList的特有方法: (一)添加方法 addFisrt(E e):将指定元素插入此列表的开头.//参数e可以理解成Object对象,因为列表可以接收任何类型的对象,所以e就是Object对 ...

  2. java集合详解(附栈,队列)

    1 集合 1.1 为什么会出现集合框架 [1] 之前的数组作为容器时,不能自动拓容 [2] 数值在进行添加和删除操作时,需要开发者自己实现添加和删除. 1.2 Collection接口 1.2.1 C ...

  3. Java笔记(六)列表和队列

    列表和队列 一)ArrayList 1.基本原理 ArrayList是一个泛型容器.内部会有一个数组elementData,一般会有预留空间 有一个整数记录实际的元素个数. private trans ...

  4. Java 集合深入理解(10):Deque 双端队列

    点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 什么是 Deque Deque 是 Double ended queue (双端队列) 的缩写,读音和 deck 一样,蛋 ...

  5. Java 集合深入理解(9):Queue 队列

    点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 今天心情不太好,来学一下 List 吧! 什么是队列 队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加.头部 ...

  6. Java 集合 散列表hash table

    Java 集合 散列表hash table @author ixenos 摘要:hash table用链表数组实现.解决散列表的冲突:开放地址法 和 链地址法(冲突链表方式) hash table 是 ...

  7. Java集合(二):List列表

    在上一节中,介绍了Java集合的总体情况.从这节開始,将介绍详细的类.这里不单单介绍类的使用方法.还会试图从源代码的角度分析类的实现.这一节将介绍List接口及实现类.即列表中的链表LinkedLis ...

  8. Java集合--阻塞队列及各种实现的解析

    阻塞队列(Blocking Queue) 一.队列的定义 说的阻塞队列,就先了解下什么是队列,队列也是一种特殊的线性表结构,在线性表的基础上加了一条限制:那就是一端入队列,一端出队列,且需要遵循FIF ...

  9. 关于Java集合类库中的几种常用队列

    Java中几种常用的队列 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞 ...

  10. Java 集合框架

    Java集合框架大致可以分为五个部分:List列表,Set集合.Map映射.迭代器.工具类 List 接口通常表示一个列表(数组.队列.链表 栈),其中的元素 可以重复 的是:ArrayList 和L ...

随机推荐

  1. 创客课堂——Scratch的操作界面

    大家好,这里是蓝精灵创客公益课堂,我是蓝老师 上期我们了解了scratch软件进行了简单的了解,很多朋友都已经按照上期方法下载安装好了软件. 那么今天蓝老师就和大家一起认识下Scratch的操作界面及 ...

  2. linux-删除一个目录下的所有文件,但保留某个或者多个指定文件

    Linux:删除一个目录下的所有文件,但保留一个指定文件 面试题:删除一个目录下的所有文件,但保留一个指定文件 解答: 假设这个目录是/xx/,里面有file1,file2,file3..file10 ...

  3. Kconfig语法简介

    背景: 上篇文章<添加自己的驱动程序到Linux内核树中>简单介绍了在Linux内核配置中添加自己的驱动选项.但是仅靠如此简单的配置有时候不能满足我们的要求. Target :hi3531 ...

  4. Django 中 app_name (应用命名空间) 和 namespace (实例命名空间) 的区别

    转自:https://www.jianshu.com/p/404500a0408a 补充理解: 先把官网上对应用命名空间(app_name)和实例命名空间(namespace)的解释贴上: app_n ...

  5. java如何在不访问数据库就可以对list分页?

    废话不多说,直接上代码 import java.util.ArrayList; import java.util.List; public class demo { public static voi ...

  6. No PostCSS Config found in报错解决

    前情提要]日前本人将本地项目上传GitHub之后,然后再clone到本地,运行是报错:Error: No PostCSS Config found in... 项目在本地打包运行的时候不报错,上传到 ...

  7. Pandas-Numpy-Scipy-Matplotlib

    Ref:Using iloc, loc, & ix to select rows and columns in Pandas DataFrames 单纯的方括号只有两个用途:基于列的label ...

  8. Java&Selenium根据实参启动相应浏览器

    Java&Selenium根据实参启动相应浏览器 /** * 定义函数initBrowser * @param browser:字符串参数chrome/ie/xx * @return 并返回驱 ...

  9. 《流畅的Python》 Sequence Hacking, Hashing and Slicing(没完成)

    序列修改,散列和切片 基本序列协议:Basic sequence protocol: __len__ and __getitem__ 本章通过代码讨论一个概念: 把protocol当成一个正式接口.协 ...

  10. 第107题:二叉树的层次遍历II

    一. 问题描述 给定一个二叉树,返回其节点值自底向上的层次遍历. (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 例如: 给定二叉树 [3,9,20,null,null,15,7], 3 ...