深入理解 ArrayList、LinkedList、HashSet 等 Java 容器_array list不能根据索引访问元素吗_haihui_yang的博客-程序员宅基地

技术标签: # Basis  ArrayList  collection  java 容器  LinkedList  HashSet  

转载请注明原创出处,谢谢!

HappyFeet的博客

Java 容器的继承关系图:

java容器的继承关系图

集合表示一组对象,称为其元素。有些集合允许重复元素,而另一些则不允许。有些是有序的,有些是无序的。 JDK 没有提供这个接口的任何直接实现:它提供了更具体的子接口(如 Set、List 和 Queue)的实现。


一、List

有序的元素序列,可以通过索引(即下标)访问元素。

1、ArrayList(基于索引的动态数组)

(1)实现了可变大小的数组,允许所有元素,包括 null 值,底层使用数组(array)保存所有元素,所以随机访问很快,可以直接通过元素的下标值获取元素的值(size、isEmpty、get、set、iterator、listIterator 这些方法的时间复杂度均为 O(1)),但插入和删除较慢,因为需要移动 array 里的元素(即 add、remove 的时间复杂度为 O(n)),未实现同步。

(2)每一个 ArrayList 实例都有一个容量(capacity),使用 Lists.newArrayList() 创建的是一个 capacity = 0 的 List,当在添加第一个元素的时候会扩展到默认的初始化容量(10),当对其添加的数据大于它的 capacity 就必须改变 ArrayList 的 capacity(一般是原来大小的 1.5 倍),而这种 resize 操作是有开销的,所以如果事先知道数组的大小为 actualSize,可以按照下面的方式初始化一个大小固定的 ArrayList,以减去 resize 的开销:

int actualSize = 100;
List<Object> objectArrayList = Lists.newArrayListWithCapacity(actualSize);

(3)iterator() 和 listIterator(int) 返回的迭代器是快速失败(fail-fast)的:

如果在迭代器创建之后,原始的 List 被修改了,迭代器会抛一个 ConcurrentModificationException,原因是 Iterator 里的 expectedModCount 和 List 的 modCount 不一致。在迭代的时候如果需要修改 List,只能通过 Iterator 的 remove 方法修改。

(4)从 Array 创建 ArrayList 的坑:

Object[] array = new Object[10];
List<Object> arrayList1 = Lists.newArrayList(Arrays.asList(array));
List<Object> arrayList2 = Arrays.asList(array);
// 需要注意的是:Arrays.asList(array) 返回的是一个 fixed size array(上面的arrayList2),如果不用 Lists.newArrayList(Arrays.asList(array))(上面的arrayList2)包装起来的话,对它进行 add 或 remove 操作就会报 java.lang.UnsupportedOperationException

2、LinkedList(双链表数据结构)

(1)实现了 List 接口,允许 null 值,底层使用链表保存所有元素(除了要存数据外,还需存 next 和 pre 两个指针,因此占用的内存比 ArrayList 多),因此,向 LinkedList 里面插入或移除元素时会特别快,但是对于随机访问方面相对较慢(需要遍历链表,遍历的时候会根据 index 选择从前往后或从后往前遍历,如果 index < (size >> 1) 则从前往后),无同步,想要实现同步可以这样:

List list = Collections.synchronizedList(new LinkedList(...)); 

(2)LinkedList 还拥有了可以使其用作堆栈(stack),队列(queue)或者双向队列(deque)的方法(拥有 pop、push,从 LinkedList 的首部或尾部添加或删除元素等方法)。

3、Vector(实现了同步的 ArrayList)

和 ArrayList 几乎一模一样,除开以下两点:

  • 实现了同步,较 ArrayList 有轻微的性能上的差距(一般不用它,而是使用 ArrayList,在外部实现同步);

  • 二者的 resize 的大小不一样:ArrayList 是变为原来的 1.5 倍,而 Vector 为原来的 2 倍。

4、Stack(Java 的堆栈实现是糟糕的,它继承了 Vector)

Stack 表示后进先出(LIFO)堆栈,继承于 Vector ,新增了五个方法:

  • push(E item):将 item 压入栈;

  • pop():remove 掉栈顶元素并返回 remove 掉的元素;

  • peek():返回栈顶(Vector 的最后一个元素)的第一个元素(无 remove 操作);

  • empty():判断栈是否为空;

  • search():返回查找到的离栈顶最近的元素的 position;

  • javadoc 中建议:Deque 接口和它的实现提供了一个更完整、更一致的 LIFO 栈操作集,应该优先使用这个类。例如:

Deque<Integer> stack = new ArrayDeque<Integer>();

5、ArrayList、LinkedList 和 Vector 总结

(1)当集合内的元素需要频繁插入,删除操作时应使用 LinkedList;当需要频繁查询时,使用 ArrayList(大部分情况是使用 ArrayList);

(2)ArrayList 和 LinkedList 都未实现同步,Vector 是在 ArrayList 的基础上实现了同步,是线程安全的;

(3)相比而言,LinkedList 占的内存要比 ArrayList 大(因为它必须维护下一个和前一个节点的链接)。

二、Set

不包含重复元素的集合。

1、HashSet

  • 由 HashMap 支持实现,无序,未实现同步,允许 null 值;

  • add, remove, contains and size:这些方法的时间复杂度为 O(1),迭代 HashSet 的时间与实际 HashSet 的 size 和内部支持实现的 HashMap 的 capacity 之和成线性关系。所以,如果对迭代性能敏感,就不要把 HashSet 的初始容量设置太高(或者负载因子太低)(实际上是减小 HashMap 的 capacity 值)。

2、LinkedHashSet

  • 由 LinkedHashMap 支持实现,有序(保证了元素的插入顺序),未实现同步,允许 null 值

  • 与 HashSet 相比:需要多维护一个 linked list ,所以总体上性能上会比 HashMap 稍微慢一点,但有一点例外:迭代 LinkedHashSet 的开销比迭代 HashSet 要小,原因是迭代 LinkedHashSet 只与 size 相关,而迭代 HashSet 还与 capacity 相关。

3、SortedSet

  • 元素有序(按照自然排序或 Comparator 排序)

  • 内部的元素都必须实现 Comparable 接口,保证集合内部任意两个元素之间是可比较的

  • subSet(E fromElement, E toElement) :返回该集合部分元素([from,to))的视图(view),操作该视图会映射到原集合上,而且该视图有限制:当添加一个此范围([from,to))之外的值会抛 IllegalArgumentException。

  • headSet(E toElement):( < toElement) :返回一个和 subSet 一样的视图([lowestElement, toElement))

  • tailSet(E fromElement):( >= fromElement) :返回一个和 subSet 一样的视图([fromElement, highestElement])

4、TreeSet(SortedSet 的实现)

  • 基于 TreeMap 的 NavigableSet 实现;未实现同步。

  • add, remove and contains :时间复杂度为 log(n)

三、Queue

为在处理之前保存元素而设计的集合。

  • 提供了额外的插入(offer)、提取(poll)和检查(peek)操作,当操作失败时返回 null 或 false,而不是抛出异常。

  • 一般来说,队列都是先进先出(FIFO)的方式,但也有例外:如优先级队列(PriorityQueue)。

  • 队列实现通常不允许插入空元素,因为 null 被 poll 方法用作特殊返回值来指示队列不包含任何元素。

1、PriorityQueue

基于优先级堆(实际上是最小堆)实现,用数组存储堆;

  • 未实现同步(线程安全的优先级队列:PriorityBlockingQueue),是无界队列,但有容量(capacity),添加元素时,如果容量不足,会自动增长。

  • iterator() 方法不提供保证以特定的顺序遍历优先级队列中的元素。

  • offer, poll, remove() and add 的时间复杂度为 O(log(n)):往堆里增删元素

  • remove(Object)contains(Object) 为线性时间:Java 文档上是写的这两个方法的复杂度都是线性时间,即 O(n) ,因为是用数组存储堆的,所以 contains 就是遍历数组查找元素,因此,contains(Object) 的复杂度是 O(n) 是没有问题的,但是我觉得 remove(Object) 操作的复杂度是 O(n) + O(log(n)),即从数组里面找到它 O(n),然后从堆里面删除它 O(log(n))。(还是我理解错了,希望有大佬能够指导一下?)

  • peek, element, and sizeO(1):因为是取堆顶元素。

2、Deque

双向队列,支持两端元素的插入与删除。Deque 也可以用作 LIFO(后进先出)堆栈。这个接口应优先于传统的 Stack 类使用。

不支持通过索引访问元素。

虽然 Deque 实现不是严格要求禁止插入 null 值,强烈建议任何允许 null 元素的 Deque 实现的用户不要利用插入空值的能力,因为null被一些方法用作特殊返回值来指示该双端队列是空的。

3、ArrayDeque(Deque 的实现)

  • 未实现同步,不允许 null 值。

  • 当用作堆栈时,该类可能比 Stack 快,并且在用作队列时比 LinkedList 快。

参考资料:

(1)Create ArrayList from array

(2)When to use LinkedList over ArrayList?

(3)java中的容器讲解

(4)Java ArrayList resize costs

(5)collections in java

(6)Why should I use Deque over Stack?

(7)Java 文档

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/haihui_yang/article/details/80247338

智能推荐

Vue 中 axios 跨域请求-程序员宅基地

1 :找到config目录下的 index.js文件2 找到 dev里 的proxyTable 属性 并修改proxyTable: { "/api":{ target: 'http://localhost:8080',//http://localhost:8080是我的javaweb务器(我的vue服务http://localhost:8081) cha...

时间戳范围内正则表达式 生成器 解决方案_正则表达式 区间范围 时间戳访问-程序员宅基地

需求说明如何求出一个正则表达式,表示在 1324736000 到 1546272000之间的数例如15423232231这个根据正则表达式能够识别出来为true实际应用这个需求是因为由于公司内部rowkey的设计导致的,rowkey为 id+timestampe如果想对整个表中的指定1324736000 到 1546272000范围内的数据进行聚合操作其中一个解决方案是用row..._正则表达式 区间范围 时间戳访问

算法竞赛入门经典 1.3 顺序结构程序设计-程序员宅基地

//程序1-6 三位数反转(1)#includeusing namespace std;int main(){ int n; cin>>n; cout<<<<>n;

friends_老友记第一季台词word-程序员宅基地

friends 原来我以为自己有很多的friends,不过后来逐渐发现friends原来~~~如比。 自从我的一个兄弟对我的老婆说了一些远距离恋爱的缺点,但我知道他说这句话的时候,偶有点失落了,我最讨厌别人给我老婆说一些负面的影响了,也许他是无心的,但我却怎么也不想原谅他,那一刻,所有的friends被我在心里划了一段距离。也许是我太在乎我老婆了。不知道 原谅我。现在的我有时会感到好孤单,没人_老友记第一季台词word

Mybatis select标签的用法_mybatis引用<select> 标签-程序员宅基地

本文转载出处:https://www.cnblogs.com/junjie2019/p/10568055.html第一步,在接口中添加方法:public interface UserMapper { SysUser selectById(Long id);}第二步,完成映射文件:<?xml version="1.0" encoding="UTF-8" ?>..._mybatis引用 标签

oracle jdbc balance,JDBC 如何配置RAC 的Load Balance ?-程序员宅基地

PURPOSE-------This document discusses how to implement load balancing from a JDBC applicationthat connects to a RAC configured system.SCOPE & APPLICATION-------------------This material is intende..._oracle jdbc blance

随便推点

Linux下编译mongo的c++链接库-程序员宅基地

1、数据库版本:3.4.9 c++库版本要求:mongocxx 3.1.x2、编译C++库 参考:https://mongodb.github.io/mongo-cxx-driver/mongocxx-v3/installation/ 需要一个支持c++11的编译器(gcc[4.8/5.4],clang,visual studio) CMake版本为

上位机——简单串口通信助手(接收文本数据代码)-苏荣意-程序员宅基地

本文章仅能实现简略版串口助手接收文本内容的代码。这样我们就可以接收都串口发送的文本内容了。定义一个判断串口是否关闭函数方法。定义一个判断串口是否打开函数方法。3)配置串口参数-打开串口。

Python算法——构建单链表,添加删除元素-程序员宅基地

构建:把链表元素和链表索引分别储存在两个list里。listvalue = [1, 5, 6, 2, 7, 3]listright = [3, 2, 4, 5, -1, 1]添加元素:元素添加时,要先让新元素指针指向后面的元素,再让他前面的元素指针指向新元素!!!!!!不然,先让前面的元素(a)指向新元素(b),再找新元素后面的元素(c)时,c的索引本来在a里,但是a已经改了,所以...

用户体验五要素_什么是用户体验五要素?-程序员宅基地

第一章:用户体验的为什么如此重要用户体验并不是指一件产品本身是如何工作的,用户体验是指“产品如何与外界发生联系并发生作用”,也就是人们如何“接触”和“使用”它。当人们询问你某个产品或服务时,他们问的是使用体验。它用起来难不难?是不是很容易学会?使用其起来感觉如何?关键的设计虽然很小,但一旦设计出错,就会失去用户。如:按下按钮时的“滴答“声似乎无关紧要,但如果这个声音决定你是否喝到了咖啡,那么它就变...

数据治理标准国际趋势_潘蓉 英国标准协会 亚太首席数据治理专家-程序员宅基地

本文根据潘蓉女士在【DQMIS 2020第四届数据质量管理国际峰会】现场演讲内容整理而成。图1.1BSI英国标准协会亚太区首席数据治理标准专家 潘蓉演讲嘉宾介绍 - 潘蓉 BSI英国标准协会亚太区首席数据治理标准专家,中国区ICT技术总监 从技术到管理,从商业项目到公益项目,从风险管理到数据治理标准专家,在BSI带领团队高绩效增长,连续获得BSI创新奖,也曾获得2004年国内首届“十大IT女性”奖(工信部与妇女联合会、中国计算机杂志)。 早在2015就出版..._潘蓉 英国标准协会 亚太首席数据治理专家

10.elasticsearch插件五graph插件安装详解-程序员宅基地

铭毅天下,原文地址:blog.csdn.net/laoyang360 https://blog.csdn.net/wojiushiwo987/article/details/51472821一、graph插件介绍graph插件一个新的用于 Elasticsearch 和 Kibana 的插件,通过它们您可以很方便的发现、理解和探索现有数据之间的关系。和 Elastic 的所有产品一样,它的 UI ...

推荐文章

热门文章

相关标签