List接口实现集合

本文主要介绍Java中List接口及其实现类的特点。List底层是序列,允许重复元素。实现类包括ArrayList、Vector和LinkedList,分别介绍了它们的底层结构、效率、线程安全性等。还重点讲解了ArrayList的快速失败机制及解决办法,以及CopyOnWriterArrayList的安全失败机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

List接口的特点

list底层结构是一个序列、存储内容时直接在内存中开辟一个空间、然后将空间地址与索引对应。
实现list接口的用户、可以直接堆列表中元素的插入位置进行确定、根据元素的索引来访问元素
允许重复元素

实现List接口的集合特点

在这里插入图片描述

ArrayList

底层数据结构-数组、支持随机访问、在内存中分配连续的空实现了数组长度可变性
遍历和查找元素效率高
增加和删除效率低、O(n)
初始容量10
非线程安全

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
        
     private static final long serialVersionUID = 8683452581122892189L;
     transient Object[] elementsDate;
      int size ;      //自动改变size机制
      
      public ArrayList(int initialCapacity)//构造一个具有指定初始容量的空列表。    
      public ArrayList()      //默认构造一个初始容量为10的空列表。    
      public ArrayList(Collection<? extends E> c)//构造一个包含指定 collection 的元素的列表

int newCapacity=oldCapacity+(oldCapacity>>1); // 1.5倍的方式扩容

Serializable是序列化的,目的是将对象状态信息持久的保存起来
在java中,对象存储在JVM堆上面、JVM运行时对象存在当JVM的停止运行、对象状态可能会丢失。想要永久的存储对象信息、因此需要读取对象信息出去、实现序列化。
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

transient 修饰的属性,不会被序列化
静态的属性能不能被序列化和反序列化
实现Serializable接口时要给serialVersionUID赋值,因为API文档里面规定描述:
如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会 InvalidClassException。
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,在反序列化时
由于计算默认的 serialVersionUID 对类的详细信息有较高的敏感性、编译器实现的不同可能千差万别,可能会抛出InvalidClassException。

ArrayList的add和remove、clear都有改变modcount,++操作

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
快速失败机制

fast-fail机制是java集合中的一种错误检测机制、会尽力的在不同步修改操作中尽最大努力去抛出异常,所以这种机制一般仅用于检测bug。

示例
1、多线程
当多线程操作一个集合、线程A 使用Iterator 遍历时、线程B改变了集合内容、线程A抛出异常。
原因:集合类通过modcount机制来记录集合被改变的次数、Iterator 遍历会判断modcount 和初始值是都一样、不一样抛出ConcurrentModificationException。
2、单线程 使用hashmap、arraylist都可能出现fast-fail错误机制

 public static void main(String[] args) {
           List<String> list = new ArrayList<>();
           for (int i = 0 ; i < 6 ; i++ ) {
                list.add(i + " ");
           }
           Iterator<String> iterator = list.iterator();
           int i = 0 ;
           while(iterator.hasNext()) {
                if (i == 3) {
                     list.remove(3);
                }
                System.out.println(iterator.next());
                i ++;
           }
     }

在iterator时、list 删除 remove一个元素,会发生fail-fast。

ArrayList中快速失败机制

在arraylist.iterator()

private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return 即将遍历元素的索引
        int lastRet = -1; // index of last element returned; -1 if no such 记录刚遍历过元素的索引
        int expectedModCount = modCount;

   public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
          final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

modCount != expectedModCount抛出该异常。
expectedModCount初始值默认等于modCount,当ArrayList进行add()、remove()、clear()等更改集合元素方法的时候、modCount ++。
当元素集合放生改变时、modCount 发生变化、使得modCount != expectedModCount、抛出异常。

ArrayList:
1、 在使用iterator遍历时、不能使用list.add() 、list.remove() 操作、只能用 iterator.remove();
2、 Iterator的设计实在迭代时不允许其他线程修改、增加了 checkForComodification();
iterator.remove()可以使用是因为iterator.remove()时自己修改自己不存在并发修改、并且迭代器remove会重置expectedModCount、将cursor向前移动一位。

解决:
1、modCount 加关键字Synchionzed
2、使用java并发包(java.util.concurrent)中的类来代替 ArrayList 和hashMap

HashMap,可以使用ConcurrentHashMap, ConcurrentHashMap采用了锁机制,是线程安全的。
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。即迭代不会发生fail-fast,但不保证获取的是最新的数据。

CopyOnWriterArrayList中非快速失败机制
安全失败是指、遍历集合时、在拷贝的集合上遍历操作、集合元素发生变化时、是不影响遍历过程的。

1、 在使用iterator遍历时、不能iterator.remove()使用操作、只能用 list.add() 、list.remove() ;
原因:当然是因为CopyOnWriterArrayList适用于多线程的,他的迭代器的实现类COWIterator会在创建时复制一份list副本、之后迭代的是父本、所以链表改变元素无影响。但 CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。
Iterator本身存在于副本、删除副本元素不存在意义、如果去删除原始list、在并发的情况下可能和创建迭代器的副本已经完全不同了

Vector

线程安全的集合、同步的
vector初始容量为10、2倍方式扩容、可指定扩容大小

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
public Vector()//使用指定的初始容量和等于0的容量增量构造一个空向量。    
public Vector(int initialCapacity)//构造一个空向量,使其内部数据数组的大小,其标准容量增量为零。    
public Vector(Collection<? extends E> c)//构造一个包含指定 collection 中的元素的向量    
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量构造一个空的向量  

LinkedList

底层数据结构-双向链表、采用链表的存储方式、不支持随机访问
遍历和查找元素效率低
增加和删除效率高
非线程安全

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    
        transient int size = 0;
        transient Node<E> first;
        transient Node<E> last;

Deque方法 addLast addFirst
offerLast offerFirst
removeLast removeFirst
pollLast pollFirst

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值