Java集合·06·List总结

一、List框架图

collection08.jpg
总结

接口:

Iterable接口:支持Iterator,定义Iterator获取方法,支持foreach-loop

Collection接口:集合接口类,定义了基本的添加(add)、删除(remove)、访问(Iterator)接口,支持Iterable

List接口:有序列表接口类,扩展自Collection,支持索引操作(add(index, element)、get(index)、indexOf(element))

Queue接口:队列接口类,扩展自Collection,先进先出,支持操作(offer()、poll()、element()、peek())

Duque接口:双端队列接口类,扩展自Queue,支持头尾两端操作

AccessRandom接口:支持随机随机访问,无API

抽象类:

AbstractCollection,implement Collection,实现了Colletion基本方法,减少Collection接口实现难度

AbstractList,extends AbstractCollection,implement List,实现了List基本方法,子类只需实现get()、set()、remove()、size()方法。

AbstractSequentialList,extends AbstractList,将index相关方法使用Iterator实现,减少对于index的依赖,将“sequential access”顺序存储简单化。实现了“链表中,根据index索引值操作链表的全部函数”

实现类:

ArrayList是一个数组队列,相当于动态数组。由数组实现,随机访问效率高,随机插入、随机删除效率低。

Vector是一个矢量队列,相当于动态数组,由数组实现。与ArrayList类似,但是Vector是线程安全的,ArrayList是非线程安全的。

Stack是栈,继承于Vector。有着先进先出(FIFO)的特点。

LinkedList是一个双向链表,也可以被当作栈、队列、或者双端队列进行操作。由链表实现,随机访问效率低,但随机插入、随机删除效率高。

二、使用场景

可以从两个方面进行考虑:

(01) 性能需求

对于需要快速插入,删除元素,应该使用LinkedList。
对于需要快速随机访问元素,应该使用ArrayList。

(02) 线程环境

如果List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList)。
如果ist可能同时被多个线程操作”,此时应该使用同步的类(如Vector)。

三、性能差异及原因

List可以分为两类,基于数组的ArrayList、Vector、Stack和基于链表的LinkedList,我们来比较下这两种实现方式对于性能的影响

1. 随机添加

ArrayList:

  1. 检查容量

  2. 扩容(可能)

  3. 移动index后的所有元素

    System.arraycopy(elementData, index, elementData, index + 1, size - index)
    
  4. 设置index位置元素

LinkedList:

  1. 根据index,从头or尾遍历链表,直到到达指定的index
  2. 添加节点

结论:随机添加/删除时,LinkedList优于ArrayList

2. 随机访问

ArrayList:

  1. 判断size
  2. 获取元素
  3. 返回元素

LinkedList:

  1. 判断size
  2. 根据index,从头or尾遍历链表,直到到达指定的index
  3. 获取元素内容
  4. 返回元素

结论:随机访问时,ArrayList由于LinkedList

四、Vector和ArrayList比较

相同点:

  • 都是List
  • 都是通过数组实现,本质上都是动态数组
  • 都实现了RandomAccess接口
  • 默认数组容量相同,都为10
  • 都支持Iterator和ListIterator

不同点:

  • 线程安全性不同,ArrayList是非线程安全,而Vector是线程安全的
  • 对序列化支持不同
  • 容量增加方式不同,Vector中有capacityIncrement
  • 对Enumeration的支持不同,Vector支持通过Enumeration去遍历,而List不支持

附1、衍生问题

1. 数组在内存中是怎么存储的?

2. foreach实现原理

根据对象不同有不同的逻辑:

  • 对于list编译器会调用Iterable接口的 iterator方法来循环遍历数组的元素,iterator方法中是调用Iterator接口的的 next()和hasNext()方法来做循环遍历。
  • 对于数组,就是转化为对数组中的每一个元素的循环引用

4. Collections.synchronizedList实现原理

Collections.synchronizedList()通过重写相关方法,添加一个互斥量mutex,方法调用之前都要尝试先去获取mutex

3. toArray方法抛出java.lang.ClassCastException

这个是个向下转型失败问题。在Java中容许向上转型和向下转型,转型结果根据java虚拟中的对象的类型的来决定的,Java虚拟机中保留了所有对象的类型,数组也是一个对象。

toArrays方法

@Override public Object[] toArray() {
        int s = size;
        Object[] result = new Object[s];
        System.arraycopy(array, 0, result, 0, s);
        return result;
    }

新创建了一个Object[]对象,类型是[Ljava.lang.Object,把[Ljava.lang.Object转换成[Ljava.lang.String是显然不可能的事情,因此Java虚拟机中只保存了这是一个Object数组,并不能保证其中每个元素都是String类型的,所以这个转型不能成功。数组里面的元素只是对象的引用,并不存储具体的元素,所以数组元素的类型还是保存在Java虚拟机中的。

根据上面的解释,我们可以把这个问题归纳到下面这个模型。

    Object objs[]=new Object[10];
    String strs[]=(String[])objs;

这样子和刚才上面编译错误是一样的,如果我们把修改一下这个代码,如下:

    String strs[]=new String[10];
    Object objs[]=strs;

这样子就可以编译通过了,所以这个问题我们可以归结为一个Java转型规则的一个问题。

详见《Java数组对范型的支持问题》http://blog.csdn.net/ithomer/article/details/7532935

5. fast-fail概念和实现方式

概念:

fast-fail是一种错误检测机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。

例如:当一个线程A使用Iterator遍历某集合的过程中,该集合的内容被其他线程改变,那么A线程访问集合时就会抛出ConcurrentModificationException异常,产生fail-fast事件。

原理:

ArrayList中持有一个modCount对象,记录该ArrayList修改次数。ArrayList中的Iterator中持有一个expectedModCount对象。通过比较两者是否相等来确定是否有其他对象修改了集合。在Iterator初始化时将expectedModCount = modCount,在对集合进行访问时,会首先进行modCount != expectedModCount判断,两者不相等时抛出ConcurrentModificationException异常,在操作结束后,更新expectedModCount = modCount

这是modCoun他的官方说明:

The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.

两类操作会影响到modCount:

  • 修改数组大小的操作,比如add()、remove()等,注意trimToSize()、ensureCapacity()也会影响。
  • 影响数组中对象结构的操作,比如replace()、sort()等。

注意点:

只能用于检测错误,因为JDK并不保证fail-fast机制一定会发生。若在多线程环境使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”

6. RandomAccess有什么用

List 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。

将操作随机访问列表的最佳算法(如 ArrayList )应用到连续访问列表(如 LinkedList )时,可产生二次项的行为。如果将某个算法应用到连续访问列表,那么在应用可能提供较差性能的算法前,鼓励使用一般的列表算法检查给定列表是否为此接口的一个 instanceof ,如果需要保证可接受的性能,还可以更改其行为。

现在已经认识到,随机和连续访问之间的区别通常是模糊的。例如,如果列表很大时,某些 List 实现提供渐进的线性访问时间,但实际上是固定的访问时间。这样的 List 实现通常应该实现此接口。

支持快速随机访问,即是支持通过下标序号进行快速访问(于是否支持get(index)不同)。RandomAccess用于标记List是否支持快速随机访问。此接口的目的是容许实现类更改访问逻辑,提供更好的顺序/随机访问性能。

在使用过程中,可以使用if(list instanceof RamdomAccess)判断List属于RandomAccess或者是SequenceAccess。两者适合不一样的遍历算法:

RandomAccess适合:

     for (int i=0, i<list.size(); i++){
       list.get(i);
     }

SequenceAccess适合:

 for (Iterator i=list.iterator(); i.hasNext(); ){
        i.next();
 }

7. 怎么实现不可修改的列表?

继承AbstractList,仅实现size()和get()方法。

8. access方式

目前共遇到了两种access方式

  • sequential access 顺序存取
  • random access 随机存取

9. CopyOnWriteArrayList实现原理

在写操作时,copy一份数组,写完成后替换原有数组

10.List的toString样式

[value1, value2, value3, value4]

11.“有序”的概念

有序可以表示两种含义:

一是指可排序

二是指每个元素都有一个对应的下标,即对某个元素在集合中的位置进行指定。

List的有序是指第二种,每个元素都有一个对应的下标,即对某个元素在集合中的位置进行指定。

TreeMap/TreeSet的有序是指第一种可排序。

附2. 基本数据接口接口介绍

Queue(FIFO (first-in-first-out))

offer(E): boolean
remove(): E
poll(): E
element(): E
peek(): E

Dueue(double ended queue) extends Queue

addFirst(E): void
addLast(E): void
offerFirst(E): boolean
offerLast(E): boolean
removeFirst(): E
removeLast(): E
pollFirst(): E
pollLast(): E
getFirst(): E
getLast(): E
peekFirst(): E
peekLast(): E
removeFirstOccurrence(Object): boolean
removeLastOccurrence(Object): boolean
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354