看图学源码 之 ArrayList 源码分析

ArrayList

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
  
}

实现的接口

Serializable

  • 类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态 序列化 或 反序列化。 可序列化类的所有子类型都是可序列化的。 序列化接口没有方法或字段,仅用于标识可串行化的语义。
  • 序列化:将对象的数据写入到文件(写对象)
  • 反序列化:将文件中对象的数据读取出来(读对象)

RandomAccess

  • 目的:可以 随机访问列表 或者 顺序访问列表的时候提供良好的性能
for (int i = 0; i < list.size(); i++) { list.get(i); }     // -----随机访问
Iterator<String> it = list.iterator();  while (it.hasNext()){  it.next(); }   // -----顺序访问

/*
ArrayList 中随机访问 更快
  
LinkedList 中 顺序访问 更快  
-- 由于随机访问的时候源码底层每次都需要进行折半的动作,再经过判断是从头还是从尾部一个个寻找。 
-- 而顺序访问只会在获取迭代器的时候进行一次折半的动作,以后每次都是在上一次的基础上获取下一个元素。 
-- 因此顺序访问要比随机访问快得多 
*/

总结: 对返回的集合进行判断,如果返回的集合实现了 RandomAccess 就使用 普通for ; 否则使用迭代器(增强for)

Cloneable

浅拷贝

1、 被克隆对象所在的类必须实现 Cloneable 接口

2、 必须重写 clone 方法

class  Student implements Cloneable{
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}
class clone_{
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student = new Student();
        Student student1 = (Student) student.clone();
        System.out.println(student == student1); // fasle
    }
}

存在的问题(局限性): 基本数据类型可以完全复制,但是引用数据类型不可以,仅仅只会拷贝一分引用

深拷贝
class  Skill implements Cloneable{

    String skillname;

    public Skill(String skillname) {
        this.skillname = skillname;
    }

    public String getSkillName() {
        return skillname;
    }

    public void setSkillName(String skillname) {
        this.skillname = skillname;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
      return super.clone();
    }

    @Override
    public String toString() {
        return "Skill{" +
                "skillname='" + skillname + '\'' +
                '}';
    }
}





class  Student implements Cloneable{
    public Skill skill;
    public  int age;

    public Student(int age,Skill skill) {
        this.age = age;
        this.skill = skill;
    }

    public Skill getSkill() {
        return skill;
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // super.clone()  深拷贝不可以直接调用 super 的方法,浅拷贝 是可以的
        //先 clone 一个学生对象
        Student stu = (Student) super.clone();
        //再clone 一个 Skill 对象
        Skill skill = (Skill)this.skill.clone();
        // 将 clone  出来的 skill_ 对象赋值给 stu 对象的成员变量
        stu.setSkill(skill);
        return  stu;
    }
    @Override
    public String toString() {
        return "Student{" +
                "skill=" + skill +
                ", age=" + age +
                '}';
    }
}




class Clone_{
    public static void main(String[] args) throws CloneNotSupportedException {
        Skill skill = new Skill("ry123");
        Student  student = new Student(23,skill);
        Object student1 = student.clone();

        System.out.println(student == student1); // fasle
        System.out.println(student1);
        System.out.println(student);

        skill.setSkillName("ry");
        System.out.println(student1);
        System.out.println(student);
    }
}
false
Student{skill=Skill{skillname='ry123'}, age=23}
Student{skill=Skill{skillname='ry123'}, age=23}
Student{skill=Skill{skillname='ry123'}, age=23}
Student{skill=Skill{skillname='ry'}, age=23}

源码分析

在这里插入图片描述

成员变量

 private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.   默认初始容量。
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 创建一个大小为 0 的 list  ,容量从 0 开始增加
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 创建一个 默认空参 的 list  ,容量从10 开始增加
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

	
    /**
     存储 ArrayList 元素的数组缓冲区。 ArrayList的容量就是该数组缓冲区的长度。
     添加第一个元素时,任何具有 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将扩展为 DEFAULT_CAPACITY。
     */
    transient Object[] elementData; // non-private to simplify nested class access

/*

// 将ArrayList实例的状态保存到流中(即将其序列化)
         private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
                // 写出元素计数以及任何隐藏的内容
                int expectedModCount = modCount;
                s.defaultWriteObject();

                // 将大小写为与 clone() 行为兼容的容量
                s.writeInt(size);

                // 按正确的顺序写出所有元素。
                for (int i=0; i<size; i++) {
                    s.writeObject(elementData[i]);
                }

                if (modCount != expectedModCount) {
                    throw new ConcurrentModificationException();
                } 
            }


*/

    /**
     * ArrayList 的大小(它包含的元素数量)
     */
    private int size;


transient 作用:被修饰的属性不会序列化到硬盘

因为 ArrayList 会扩容,其中元素可以删除但是集合不会缩容,所以不序列化到硬盘就可以减少占用的硬盘,减少大量删除之后出现的 null 值,可以优化磁盘

构造函数

/**
 * 构造一个具有指定初始容量的空列表。
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

/**
 * 构造一个初始容量为 10 的空列表
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  // 默认的空数组
}

/**
 * 构造一个包含指定集合的元素的列表,按照集合迭代器返回这些元素的顺序排列。
 */
public ArrayList(Collection<? extends E> c) {
 // 把集合变为数组,然后赋值给 ArrayList 
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
          //调用copy 进行赋值
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else { //a.length == 0  传入的是空的集合
        // 那么赋值一个容量为 EMPTY_ELEMENTDATA 类型的空数组
        elementData = EMPTY_ELEMENTDATA;
    }
}

//将集合转数组的方法
public Object[] toArray() {
//调用数组工具类方法进行拷贝
  return Arrays.copyOf(elementData, size);
}

添加方法

add(E e)

在这里插入图片描述

/*
该列表的结构被修改的次数,迭代时的修改会产生 ConcurrentModificationException来响应next 、 remove 、 previous 、 set或add操作,是快速失败行为,而不是在迭代过程中面对并发修改时的非确定性行为。

子类对该字段的使用是可选的。如果子类希望提供快速失败迭代器(和列表迭代器),那么它只需在其add(int, E)和remove(int)方法(以及它覆盖的导致结构性错误的任何其他方法)中增加此字段。列表的修改)。对add(int, E)或remove(int)单次调用必须向该字段添加不超过 1 个,否则迭代器(和列表迭代器)将抛出虚假的ConcurrentModificationExceptions 。如果实现不希望提供快速失败迭代器,则可以忽略该字段。
*/
protected transient int modCount = 0;


// 将指定元素追加到此列表的末尾。
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    
  	elementData[size++] = e; // 数组操作,后移 赋值
    return true;
}

	//扩容 + 数组拷贝 、数组容量判断、初始化数组
private void ensureCapacityInternal(int minCapacity) {
      ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

//计算数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 要是 这里的条件是成立:elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    return Math.max(DEFAULT_CAPACITY, minCapacity);  // 返回的就是 max (10 , minCapacity) 直接扩容到 10 
  }
  // 要是 elementData == EMPTY_ELEMENTDATA   返回的就是 minCapacity,从 0 开始一步步扩容上去
  return minCapacity;
}


private void ensureExplicitCapacity(int minCapacity) {  // minCapacity = size +1  ,加1:可以进行提前扩容,为下一次做准备
  	modCount++;  // 记录修改次数
  	// overflow-conscious code
  	if (minCapacity - elementData.length > 0)
    grow(minCapacity); // 进行扩容
}

/**
 * 要分配的数组的最大大小。一些虚拟机在数组中保留一些头字。尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超出 VM 限制
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

// 扩容方法的具体实现
private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);  // 大约1.5 倍,源码的 原数组+原数组右移一位
  if (newCapacity - minCapacity < 0)  
    newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity); // 数组的上限
  // minCapacity is usually close to size, so this is a win:
  elementData = Arrays.copyOf(elementData, newCapacity);  //移动旧的数组元素到新数组并返回新数组 
}

 private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}


public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    // 创建指定长度的某种类型的数组。
T[] copy = ((Object)newType == (Object)Object[].class) ? 
  										(T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    // 调用本地方法将旧数组元素移动到新数组中
    System.arraycopy(original,  0,         copy,        0,          Math.min(original.length, newLength));
  								// 原数组   原数组开始位置  目标数组   目标数组的起点  移动的长度
    // 返回新数组
    return copy;
}

add(int index, E element)

在这里插入图片描述

/**
 * 在此列表中的指定位置插入指定元素。将当前位于该位置的元素(如果有)和任何后续元素向右移动(将其索引加一)
 */
public void add(int index, E element) {
    rangeCheckForAdd(index); // 检查下标

    ensureCapacityInternal(size + 1); //扩容 、数组容量判断、初始化数组  指定位置之前的数组进行拷贝
    System.arraycopy(elementData, index, elementData, index + 1, size - index); // 指定位置之后的数组进行拷贝
    elementData[index] = element;  
    size++;
}

//   检查下标是否越界
 private void rangeCheck(int index) {
   if (index >= size)  // size  是数组的实际容量  length  才是数组的长度
     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//越界提示
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

addAll(Collection<? extends E> c)

在这里插入图片描述

public boolean addAll(Collection<? extends E> c) {
  // 转为数组
    Object[] a = c.toArray();
    int numNew = a.length;
  //扩容 、数组容量判断、初始化数组  指定位置之前的数组进行拷贝
    ensureCapacityInternal(size + numNew);  // Increments modCount
  //拷贝移动数组
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}
addAll(int index, Collection<? extends E> c)

在这里插入图片描述

public boolean addAll(int index, Collection<? extends E> c) {
  	//检查元素是不是越界 
  	rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
  	// 扩容 、数组容量判断、初始化数组  指定位置之前的数组进行拷贝
    ensureCapacityInternal(size + numNew);  // Increments modCount

  	//移动的数量
    int numMoved = size - index;
    if (numMoved > 0)
      //从 下标位置开始后移 numMoved 个位置
        System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
  // 要是移动的元素个数是0 ,就直接添加到后面
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}
设置方法
set(int index, E element)

在这里插入图片描述

public E set(int index, E element) {
  // 检查是否越界
    rangeCheck(index);
	// 获取原来位置的元素
    E oldValue = elementData(index);
  // 新值取代 旧值
    elementData[index] = element;
  // 返回旧值
    return oldValue;
}

   E elementData(int index) {
        return (E) elementData[index];
    }

获取方法

get(int index)
public E get(int index) {
    rangeCheck(index);  // 越界检查

    return elementData(index);  // 返回对应的 下标位置 的元素
}

删除方法

remove(int index)

在这里插入图片描述

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

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

   // 计算得到需要移除的元素的位置
    int numMoved = size - index - 1;
  // 删除 最后的一个元素 就 = 0,不用移动了
    if (numMoved > 0)
          // 开始移动元素       原数组   (源地址)删除元素的后一个位置为  		目标数组   		(目标位置)删除元素的位置   	 移动元素的个数(尺寸)
        System.arraycopy(elementData, index+1,                      elementData,	     index,                    numMoved);
  //将后面的元素置为 null  方便进行 gc 回收
  elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}
// 找到这个位置的元素
E elementData(int index) {
  return (E) elementData[index];
}
remove(Object o)

只会移除第一个 匹配到的元素

在这里插入图片描述

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
      //顺序遍历删除 找到对应的元素就直接删掉
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
// 根据下标 移动(copy) 中的元素
 private void fastRemove(int index) {
   modCount++;
   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
}
removeAll(Collection<?> c)

在这里插入图片描述

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);  // 判空   空则抛出空指针
    return batchRemove(c, false);
}

public static <T> T requireNonNull(T obj) {
  if (obj == null)
    throw new NullPointerException();
  return obj;
}

// 批量删除
 private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
          	// 遍历
            for (; r < size; r++)
              // 数组中的元素,要是 elementData[r] 不在 要删除的元素集合中,
                if (c.contains(elementData[r]) == complement)
                 // 将这个元素 覆盖到 elementData 中
                    elementData[w++] = elementData[r];
        } finally {
          // c.contains 出现 异常的时候  就会导致 r != size ,此时 w 只等于异常出现之前满足条件时记录的元素个数,
            if (r != size) {  // r != size判断是为了在出现异常的情况下也可以正常的进行元素移除
                System.arraycopy(elementData, r, elementData, w,  size - r); // elementData 没有全部删除完,只删除了一部分,没删除的部分要进行前移
                w += size - r;
            }
          // 要是有元素被删除,就将 w 下标之后的元素 置为 null  方便进行 gc 
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;  // 修改次数就是操作的次数
                size = w;
                modified = true;
            }
        }
   // 都不满足就直接返回原数组 
   return modified;
    }

removeRange(int fromIndex, int toIndex)

在这里插入图片描述

//删除一个范围的元素
protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,  numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

清空方法

clear()

在这里插入图片描述

public void clear() {
    modCount++;
  // 遍历置为 null 方便进行垃圾回收
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
// size 置为 0 
    size = 0;
}

是否包含

contains(Object o)

底层也是通过循环遍历集合,取出一个个的元素和要找的元素进行比较

在这里插入图片描述

/*
如果此列表包含指定元素,则返回true 。当且仅当此列表包含至少一个元素 e 且满足(o==null ? e==null : o.equals(e)) 时,才返回true 
*/
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}
/*
返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1。更正式地说,返回满足(o==null ? get(i)==null : o.equals(get(i))) 的最低索引i ,如果没有这样的索引,则返回 -1 。
  */
 public int indexOf(Object o) {
   // 如果元素是null,也进行遍历操作  因为集合中有可能够会存储null
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
   //如果没有走if,也没有走else,那么就说明o该元素在集合中不存在
        return -1;
    }

继承自 AbstractCollection 的 toString()

public String toString() {
    Iterator<E> it = iterator();
  //没有元素:   返回 "[]"
    if (! it.hasNext())
        return "[]";

  // 创建  StringBuilder
    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
      //无限循环找到下一个元素
        E e = it.next();
      // sb添加元素  e == this防止自己引用自己
        sb.append(e == this ? "(this Collection)" : e);
      // 没有下一个元素了
        if (! it.hasNext())
          //结尾标记符,并在 将整个缓冲区转为 String 
            return sb.append(']').toString();
      // sb添加 标记符号
        sb.append(',').append(' ');
    }
}

迭代器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

public Iterator<E> iterator() {
    return new Itr();
}

// 一个内部类
 private class Itr implements Iterator<E> {
        int cursor;       // 下一个要返回元素的索引。默认 0 
        int lastRet = -1; // 最后一个返回元素的索引
   // 在迭代的过程中,只要实际修改次数和预期修改次数不一致就会产生并发修改异常,
   // 由于expectedModCount是Itr的成员变量,那么只会被赋值一次!!!
        int expectedModCount = modCount;  //expectedModCount预期的修改次数   modCount实际的修改次数

        Itr() {}

   // 判断集合是不是 有元素
        public boolean hasNext() { 
            return cursor != size;  // 索引是否达到 数组长度 
        }

        @SuppressWarnings("unchecked")
        public E next() {
          
            checkForComodification(); 
          /*
          有没有出现  ConcurrentModificationException 问题 ,就是根据  modCount != expectedModCount判断的
         	 final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
          */
            int i = cursor;  //下一个元素的索引赋值给i
            if (i >= size)  // 索引达到 数组长度,表示没有下一个元素了
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData; //集合底层存储数据的数组的地址 赋值 给 局部变量 []elementData
            if (i >= elementData.length)
                throw new ConcurrentModificationException(); // 下一个元素的索引 大于 集合底层存储元素的长度 并发修改异常
          //索引后移  
          cursor = i + 1;
          // 取出数组中的 元素 并 返回
            return (E) elementData[lastRet = i];
        }

   // 迭代器的删除方法
        public void remove() {
          // 最后返回元素的索引是不是  < 0 ;满足条件就产生 非法状态异常
            if (lastRet < 0)
                throw new IllegalStateException();
          // 检查 是否会产生并发修改异常 
            checkForComodification();

         // 尝试执行 ArrayList 的 remove() 方法,将 lastRet 所指向的元素从 ArrayList 中移除。
          
            try {
              // 调用  ArrayList 的remove 进行删除
                ArrayList.this.remove(lastRet);
              // 下一个要返回元素的索引   就是  最后一个返回元素的索引
                cursor = lastRet;
              // 最后一个返回元素的索引 置为 -1 
                lastRet = -1;
              // 赋值  ==> 此时 不管集合自身是否删除成功 ;实际修改次数和预期修改次数又一致了,所以并不会产生并发修改异常
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
   //用于对ArrayList中的元素进行迭代操作,并将每个元素传递给指定的Consumer函数。
        public void forEachRemaining(Consumer<? super E> consumer) {
          
          //首先检查传入的consumer是否为null,如果是null则抛出NullPointerException
            Objects.requireNonNull(consumer);
          //获取ArrayList的大小和当前游标位置。
            final int size = ArrayList.this.size;
            int i = cursor;
          //如果游标位置大于等于ArrayList的大小,表示已经迭代完所有元素,直接返回。
            if (i >= size) {
                return;
            }
          //获取ArrayList的元素数组elementData。
            final Object[] elementData = ArrayList.this.elementData;
          //如果游标位置大于等于elementData的长度,表示在迭代过程中ArrayList发生了结构性变化(比如增加或删除元素),抛出ConcurrentModificationException异常。
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
          //当游标位置不等于ArrayList的大小   &&   modCount等于expectedModCount时
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);  // 将元素传递给consumer。
            }
            // update once at end of iteration to reduce heap write traffic
          // 更新游标位置cursor和上一个返回的元素位置lastRet。
            cursor = i;
            lastRet = i - 1;
          //调用checkForComodification方法检查是否发生了结构性变化,如果发生了则抛出ConcurrentModificationException异常。
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
特色
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
    Integer next = iterator.next();
    System.out.println(next);
    if (next == ArrayList中的元素值){
        // 此处 会发生 并发修改异常,原因,就在于  底层判断 是否有并发修改异常 是根据  modCount != expectedModCount 判断的
        // 但是这里执行了  ArrayList 的remove  remove 的底层 会对 modCount++  从而就导致了 modCount != expectedModCount
        // 所以就有 有并发修改异常 
        list1.remove(ArrayList中的元素值);
    }
}

//ArrayList中的元素值 不是 list 中的 倒数第二个值

注意:

  • 结论:

    只有当要删除的元素在集合的倒数第二个位置的时候,不会产生并发修改异常

  • 原因:
    是因为在调用hasNext()方法的时候,光标的值和集合的长度一样,那么就会返回false;因此就不会再去调用next()获取集合的元素,既然不会调用next()那么底层就不会产生并发修改异常

  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值