java集合~List体系总结、ArrayList分析

来源:互联网 发布:手机建模软件 犀牛 编辑:程序博客网 时间:2024/05/26 23:00
小光光的梦 2017-09-05 12:27

一:List的整体框架图

java集合~List体系总结、ArrayList分析

线条简单说明:

1、上图中虚线且无依赖字样、说明是直接实现的接口

2、虚线但是有依赖字样、说明此类依赖与接口、但不是直接实现接口

3、实线是继承关系、类继承类、接口继承接口

类或接口说明:

1、Collection:高度抽象出来的集合、定义某一类集合所具有的基本的方法、标准。

2、Iterable:标识性接口、要求子类提供获取Iterator方法、并且要实现Iterator具有的几个方法。

3、Iterator:迭代器、用于迭代Collection中元素、要求子类必须实现获取Iterator的方法、

4、ListIterator:用于迭代List集合的迭代器、要求List子类必须实现获取ListIterator方法、并且实现其必须方法。

5、List:以队列的形式存储、操作元素、定义了这种形式的集合所具有的基本方法、以及方法的定义。要求List实现类集合中每个元素都有索引、索引值从0开始、

6、Queue:以队列的数据结构存储、操作元素、Queue对于插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null 或false,具体取决于操作)。

7、Deque:实现Queue、使子类可以以双向链表的数据结构形式存储、操作数据、从这可以看出其子类的灵活性较大。

8、Enumeration:枚举、用于Vector及其子类迭代元素、他避免了fail-fast机制、使得Vector及其子类在迭代元素的时候可以保证线程安全。

9、AbstractCollection:Collection的实现类、要求需要实现Collection接口的类都必须从它继承、目的是用于简化编程。

10、 AbstractList:继承AbstractCollection、实现List接口中定义方法、目的也是简化编程、并且其内部提供了获取Iterator、ListIterator的方法。

11、 AbstractSequencedList:继承AbstractList、使得List支持有序队列、比如链表形式存储操作元素。

12、 ArrayList:继承AbstractList、以动态数组的形式存储、操作元素、

13、 LinkedList:继承AbstractSequencedList、实现Deque、List接口、以双向链表的形式存储、操作元素。

14、 Vector:继承AbstractList、以动态数组的形式存储、操作元素、线程安全

15、 Stack:继承Vector、在Vector的基础上新增以栈的形式存储、操作元素。

二:LinkedList与ArrayList

1、相同之处

a)都直接或者间接继承了AbstractList、都支持以索引的方式操作元素

b)都不必担心容量问题、ArrayList是通过动态数组来保存数据的、当容量不足时、数组会自动扩容、而LinkedList是以双向链表来保存数据的、不存在容量不足的问题

c) 都是线程不安全的、一般用于单线程的环境下、要想在并发的环境下使用可以使用Collections工具类包装。

2、不同之处

a)ArrayList是通过动态数组来保存数据的、而LinkedList是以双向链表来保存数据的

b)相对与ArrayList而言、LinkedList实现了Deque接口、Deque继承了Queue接口、同时LinkedList继承了AbstractSequencedList类、使得LinkedList在保留使用索引操作元素的功能的同时、也实现了双向链表所具有的功能、这就决定了LinkedList的特定

c)对集合中元素进行不同的操作效率不同、LinkedList善于删除、添加元素、ArrayList善于查找元素。本质就是不同数据结构之间差异。

三:ArrayList与Vector

1、相同之处:

a) 都是继承AbstractList、拥有相同的方法的定义、

b)内部都是以动态数组来存储、操作元素的、并且都可以自动扩容。

2、不同之处:

a) 线程安全:ArrayList是线程不安全的、适用于单线程的环境下、Vector是线程安全的、使用与多线程的环境下。

b)构造方法:Vector有四个构造方法、比ArrayList多一个可以指定每次扩容多少的构造方法

c) 扩容问题:每当动态数组元素达到上线时、ArrayList扩容为:“新的容量”=“(原始容量x3)/2 + 1”、 而Vector的容量增长与“增长系数有关”,若指定了“增长系数”,且“增长系数有效(即,大于0)”;那么,每次容量不足时,“新的容量”=“原始容量+增长系数”。若增长系数无效(即,小于/等于0),则“新的容量”=“原始容量 x 2”。

d) 效率问题:因为Vector要同步方法、这个是要消耗资源的、所以效率会比较低下

e)Vector为摆脱fail-fast机制、自己内部多提供了一种迭代方法Enumeration、

四:LinkedList、ArrayList、Vector、Stack、Array

1、不同操作的效率对比

关于上面四个集合加一个数组、在这里给出一个表格用于表示他们的不同的操作的效率的排名、这样更直观、

 

实现机制

随机访问

迭代操作

插入操作

删除操作

数组

连续内存区保护元素

1

不支持

不支持

不支持

ArrayList

以数组保存元素

2

2

2

2

Vector

以数组保存元素

3

3

3

3

Stack

以数组保存元素

3

3

3

3

LinkedList

以链表保存元素

4

1

1

1

通过实例来验证上面表格的内容、由于数组比较特殊、他是牺牲的长度的变化直接在内存中开辟空间来存储元素、所以查询效率是毋庸置疑的、同时由于size一旦确定就不能改变、所以插入删除不支持。所以下面验证没有关于Array的的验证

不同的运行环境、差异可能比较大。

2、差异原因分析:

在这里不会主要讨论所有的差异、而是通过源码的方式分析LinkedList与Arraylist、ArrayList与Vector在随机访问、插入、删除元素方面的差异原因、至于迭代Iterator、他们都是用从AbstractList继承的获取Iterator方法、差异不大、不再比较。

ArrayList与LinkedList

a)ArrayList的随机访问效率高于LinkedList:

随机访问是通过索引去查找元素的、LinkedList关于获取指定索引处值的

对比两者源码可以看出、LinkedList获取指定索引处的值是通过二分法先确定索引所在范围之后、在逐个查找、直到找到指定索引处、并且对每个索引都是如此、相比于ArrayList直接定位到index处的值来讲、无疑是非常浪费时间、消耗资源的、

b)ArrayList的插入、删除操作效率低于LinkedList的原因:

对于指定index处的插入、删除、ArrayList和LinkedList都是先通过索引查找到指定位置、然后进行下一步的插入删除操作、上面我们知道LinkedList是先通过二分法查找index范围再确定index具体位置、但是ArrayList是直接定位到index处、为什么LinkedList反而快?依然通过源码找原因。

对比上面代码可以看出来ArrayList每当插入一个元素时、都会调用System.arraycopy()将指定位置后面的所有元素后移一位、重新构造一个数组、这是比较消耗资源的、而LinkedList是直接改变index前后元素的上一个节点和下一个节点的引用、而不需要动其他的东西、所以效率很高。

ArrayList与Vector:

ArrayList、Vector都是继承与AbstractList、并且在类结构上没有多少差异、但是因为Vector要同步方法、所以在性能上不如ArrayList、从源码也可以看出Vector许多方法都是使用关键字synchronized修饰的。不再贴源码

总结:

学以致用、最后总结下上述List集合体系的各个类的使用环境:

1、当需要对集合进行大量的查询时、并且是单线程环境下使用ArrayList

2、当需要对集合进行大量添加、删除时、并且是单线程环境下使用LinkedList、

3、当多线程时、需要对集合进行大量的查询时、可以考虑使用Vector或者Stack、但是不建议、我们可以使用多次提到的Collections类包装。

ArrayList简介

ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆

几点总结

关于ArrayList的源码,给出几点比较重要的总结:

1、注意其三个不同的构造方法。无参构造方法构造的ArrayList的容量默认为10,带有Collection参数的构造方法,将Collection转化为数组赋给ArrayList的实现数组elementData。

2、注意扩充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量),而后用Arrays.copyof()方法将元素拷贝到新的数组(详见下面的第3点)。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList。

3、ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法。我们有必要对这两个方法的实现做下深入的了解。

首先来看Arrays.copyof()方法。它有很多个重载的方法,但实现思路都是一样的,我们来看泛型版本的源码:

  1. public static <T> T[] copyOf(T[] original, int newLength) {

  2. return (T[]) copyOf(original, newLength, original.getClass());

  3. }

很明显调用了另一个copyof方法,该方法有三个参数,最后一个参数指明要转换的数据的类型,其源码如下:

  1. public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {

  2. T[] copy = ((Object)newType == (Object)Object[].class)

  3. ? (T[]) new Object[newLength]

  4. : (T[]) Array.newInstance(newType.getComponentType(), newLength);

  5. System.arraycopy(original, 0, copy, 0,

  6. Math.min(original.length, newLength));

  7. return copy;

  8. }

这里可以很明显地看出,该方法实际上是在其内部又创建了一个长度为newlength的数组,调用System.arraycopy()方法,将原来数组中的元素复制到了新的数组中。

下面来看System.arraycopy()方法。该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了c语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。

4、注意ArrayList的两个转化为静态数组的toArray方法。

第一个,Object[] toArray()方法。该方法有可能会抛出java.lang.ClassCastException异常,如果直接用向下转型的方法,将整个ArrayList集合转变为指定类型的Array数组,便会抛出该异常,而如果转化为Array数组时不向下转型,而是将每个元素向下转型,则不会抛出该异常,显然对数组中的元素一个个进行向下转型,效率不高,且不太方便。

第二个,<T> T[] toArray(T[] a)方法。该方法可以直接将ArrayList转换得到的Array进行整体向下转型(转型其实是在该方法的源码中实现的),且从该方法的源码中可以看出,参数a的大小不足时,内部会调用Arrays.copyOf方法,该方法内部创建一个新的数组返回,因此对该方法的常用形式如下:

  1. public static Integer[] vectorToArray2(ArrayList<Integer> v) {

  2. Integer[] newText = (Integer[])v.toArray(new Integer[0]);

  3. return newText;

  4. }

5、ArrayList基于数组实现,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。

6、在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理,ArrayList中允许元素为null。

阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 美的空调出现p0怎么办 薯片受潮不脆了怎么办 泡过的莲子煮不烂怎么办 绿豆有煮不熟的怎么办 吃了羊肉吃西瓜怎么办 吃了狗肉和绿豆怎么办 做的衣柜没有门怎么办 蒸馒头熟了会瘪怎么办 3dmax贴图太大了怎么办 嘴皮边缘颜色深怎么办 嘴巴周围肤色暗沉怎么办 中奖彩票被洗了怎么办 牙龈下面长米粒肉疙瘩怎么办 书画印章盖反了怎么办 金龙鱼一个月不吃东西怎么办 罗汉鱼头撞扁了怎么办 房顶开槽埋线白色不一样怎么办 顶上灯挪位置线怎么办 马蜂窝弄掉又来怎么办 蜂窝弄掉又有怎么办 2018年小龙虾底板脏怎么办 一本分数线擦边过怎么办 玩具塑料球扁了怎么办 胶皮与海绵开了怎么办 安卓不支持flash了怎么办 看视频要加载flash怎么办 下水道管子铁皮破了怎么办 炸金花牌一样大怎么办 玩棋牌游戏输了怎么办 苹果7插耳机外放怎么办 出国种菠菜抓了怎么办 在菲做菠菜抓到怎么办 3串1中两个怎么办 微博账号封停怎么办 阴阳师账号被永久封停怎么办 寒刃2账号被禁用怎么办 输了好多钱我该怎么办 亲朋打鱼别处在玩怎么办 做糯米蛋的蛋清怎么办 水田地没耙地平怎么办 宝宝拉鸡蛋花样大便怎么办