热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java源码分析之集合框架ArrayList03

java源码分析之集合框架ArrayList03ArrayList就是传说中的动态数组,就是Array的复杂版本,它提供了如下一些好处:动态的增加和减少元素、灵活的设置数组的大小


java源码分析之集合框架 ArrayList 03

这里写图片描述

ArrayList就是传说中的动态数组,就是Array的复杂版本,它提供了如下一些好处:动态的增加和减少元素、灵活的设置数组的大小……

首先看到对ArrayList的定义:

public class ArrayList extends AbstractList  implements List, RandomAccess, Cloneable, java.io.Serializable  


从ArrayList可以看出它是支持泛型的,它继承自AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable接口。

AbstractList提供了List接口的默认实现(个别方法为抽象方法)。 
List接口定义了列表必须实现的方法。

RandomAccess是一个标记接口,接口内没有定义任何内容。

实现了Cloneable接口的类,可以调用Object.clone方法返回该对象的浅拷贝。

通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。序列化接口没有方法或字段,仅用于标识可序列化的语义。

ArrayList的属性 

ArrayList定义只定义类4个私有属性:

  /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10; //数组初始时大小


    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};


    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    private transient Object[] elementData;//elementData存储ArrayList内的元素


    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size; //size表示它包含的元素的数量


有个关键字需要解释:transient

Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。 
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。

有点抽象,看个例子应该能明白:

public class UserInfo implements Serializable {  
private static final long serialVersiOnUID= 996890129747019948L;
private String name;
private transient String psw;

public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
}

public String toString() {
return "name=" + name + ", psw=" + psw;
}
}

public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("张三", "123456");
System.out.println(userInfo);
try {
// 序列化,被设置为transient的属性没有被序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"UserInfo.out"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try {
// 重新读取内容
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"UserInfo.out"));
UserInfo readUserInfo = (UserInfo) in.readObject();
//读取后psw的内容为null
System.out.println(readUserInfo.toString());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}


被标记为transient的属性在对象被序列化的时候不会被保存。

接着回到ArrayList的分析中……

ArrayList的构造方法

看完属性看构造方法。ArrayList提供了三个构造方法:

/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity <0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}

/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}

/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}

第一个构造方法使用提供的initialCapacity来初始化elementData数组的大小。

第二个构造方法调用第一个构造方法并传入参数10,即默认elementData数组的大小为10。

第三个构造方法则将提供的集合转成数组返回给elementData(返回若不是Object[]将调用Arrays.copyOf方法将其转为Object[])。


ArrayList的其他方法:

trimToSize():

/**
* Trims the capacity of this ArrayList instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an ArrayList instance.
*/
public void trimToSize() {
modCount++;
if (size elementData = Arrays.copyOf(elementData, size);
}
}

trimToSize()的作用是节省内存空间,因为数组是恒长的,正常如果

int[] arr = new int[20];  //[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

        不管数组里有意义的数有多少,arr数组一定占有20个 大小的内存,而ArrayList 则是变长的,因为ArrayList是先初始化一个长度为10 的数组,如果长度不够会自动扩容,这是很好的一个优点,但是有个小瑕疵,就是ArrayList每次扩容都会花费一定的开销,所以可以估计一下存储的数组大小,直接给出长度,当长度估计过大时,就可以使用这个trimToSize()方法,将没有用到的意义为空的数组的位置去除,可以节省空间。


add(E e)

add(E e)都知道是在尾部添加一个元素,源代码如下:

/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}


private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) { //判断elementData是否为空,若为空,则初始化为长度为10 的数组
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// DEFAULT_CAPACITY=10

}

ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)//为true时,需要扩容
grow(minCapacity);
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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;//ArrayList数组的最大长度为Integer.MaxValue= 0x7fffffff
}


ArrayList是基于数组实现的,属性中也看到了数组,

   比如就这个添加元素的方法,如果数组足够大,则在将某个位置的值设置为指定元素即,如果数组容量不够了呢?

   看到add(E e)中先调用了ensureCapacityInternal(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增。例如初次添加时,elementData[] 的长度为10,size为0,add将elementData[0]赋值为e,然后size设置为1(类似执行以下两条语句elementData[0]=e;size=1)。将元素的索引赋给elementData[size],会检查数组越界的情况,这里关键就在ensureExplicitCapacity(minCapacity)中了,该函数用来判断数组是否越界。如果数组长度超过现有的长度,则继续扩容,grow(minCapacity),每次扩容为原来的1.5倍,一直扩容到Integer.MaxValue的值,因为数组所以是Integer型,最大也就是Integer.MaxValue,所以ArrayList的最大长度是Integer.MaxValue+1(假如虚拟机的内存大小很大);

注意:容量的拓展将导致数组元素的复制,多次拓展容量将执行多次整个数组内容的复制。若提前能大致判断list的长度,最好提前估计数组长度并给予一个初始化数组长度,将有效的提高运行速度。可以理解提前分配好空间可以提高运行速度。

add(int index, E element) 

add(int index,E element)在指定位置插入元素。

public void add(int index, E element) {
rangeCheckForAdd(index);

ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//判断数组索引是否越界
private void rangeCheckForAdd(int index) {
if (index > size || index <0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
          首先调用rangeCheckForAdd()判断指定位置index是否超出elementData的界限,之后调用ensureCapacityInternal调整容量(若容量足够则不会拓展),然后调用System.arraycopy将elementData从index开始的size-index个元素复制到index+1至size-index的位置(即index开始的元素都向后移动一个位置),然后将index位置的值指向element。

addAll(Collection c)

addAll(Collection c) 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。

public boolean addAll(Collection 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;
}

    先将集合c转换成数组,根据转换后数组的程度和ArrayList的size拓展容量,之后调用System.arraycopy方法复制元素到elementData的尾部,调整size。根据返回的内容分析,只要集合c的大小不为空,即转换后的数组长度不为0则返回true。

addAll(int index, Collection c) 

addAll(int index, Collection c) 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。

public boolean addAll(int index, Collection c) {
rangeCheckForAdd(index);

Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount

int numMoved = size - index;
if (numMoved > 0)
//先将原数组后移numNew长度
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);

//再将a数组复制到原数组的index到numNew的位置
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
      先判断index是否越界。然后判断容量大小是足够加入c ,不够则扩容。其他内容与addAll(Collection< ?  extends E> c)基本一致,只是复制的时候先将index开始的元素向后移动index+numNew(numNew为c转为数组后的长度)个位置(也是一个复制的过程),之后将数组a内容复制到elementData的index位置至numNew。



clear()

clear()移除此列表中的所有元素。

 public void clear() {
modCount++;

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

size = 0;
}
        clear的时候并没有修改elementData的长度(好不容易申请、拓展来的,凭什么释放,留着搞不好还有用呢。这使得确定不再修改list内容之后最好调用trimToSize来释放掉一些空间),只是将所有元素置为null,size设置为0。


clone()

clone() 返回此ArrayList 实例的浅表副本。

public Object clone() {//浅复制        try {
@SuppressWarnings("unchecked")
ArrayList v = (ArrayList) super.clone();

/*以使副本具有指定size的长度。对于在原数组和副本中都
有效的所有索引,这两个数组将包含相同的值。即浅复制
*/
v.elementData = Arrays.copyOf(elementData, size);

v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
     调用父类的clone方法返回一个对象的副本,将返回对象的elementData数组的内容赋值为原对象elementData数组的内容,将副本的modCount设置为0。


contains(Object o) 

contains(Object o)   如果此列表中包含指定的元素,则返回true

    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
    
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i                 if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i                 if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    
     public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
      indexOf方法返回值与0比较来判断对象是否在list中。接着看indexOf。 通过遍历elementData数组来判断对象是否在list中,若存在,返回index([0,size-1]),若不存在则返回-1。所以contains方法可以通过indexOf(Object)方法的返回值来判断对象是否被包含在list中。 既然看了indexOf(Object)方法,接着就看lastIndexOf,光看名字应该就明白了返回的是传入对象在elementData数组中最后出现的index值。采用了从后向前遍历element数组,若遇到Object则返回index值,若没有遇到,返回-1。


ensureCapacity(int minCapacity)

ensureCapacity(int minCapacity) 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。

public void ensureCapacity(int minCapacity) {        int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;

if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}



get(int index)

get(int index) 返回此列表中指定位置上的元素。

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

return elementData(index);
}

private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
 RangeCheck检查一下是不是超出数组界限了,超出了就抛出IndexOutBoundsException异常。


indexOf(Object o)

indexOf(Object o)返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。

public int indexOf(Object o) {        if (o == null) {
for (int i = 0; i if (elementData[i]==null)
return i;
} else {
for (int i = 0; i if (o.equals(elementData[i]))
return i;
}
return -1;
}



isEmpty()

isEmpty()如果此列表中没有元素,则返回 true

 public boolean isEmpty() {        return size == 0;
}


remove(int index)

remove(int index)移除此列表中指定位置上的元素。

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;
}
    首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素。


remove(Object o)

remove(Object o)移除此列表中首次出现的指定元素(如果存在)。

    public boolean remove(Object o) {        if (o == null) {
for (int index = 0; index if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}

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
}
      首先通过代码可以看到,当移除成功后返回true,否则返回false。remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。为什么找到了元素就知道了index,不通过remove(index)来移除元素呢?因为fastRemove跳过了判断边界的处理,因为找到元素就相当于确定了index不会超过边界,而且fastRemove并不返回被移除的元素。下面是fastRemove的代码,基本和remove(index)一致。


removeRange(int fromIndex, int toIndex)

removeRange(int fromIndex, int toIndex)移除列表中索引在fromIndex(包括)和 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 elementData[i] = null;
}
size = newSize;
}
     执行过程是将elementData从toIndex位置开始的元素向前移动到fromIndex,然后将newNum位置之后的元素全部置空顺便修改size。

注意:这个方法是protected

先看下面这个例子

ArrayList ints = new ArrayList(Arrays.asList(0, 1, 2,3, 4, 5, 6));  
// fromIndex low endpoint (inclusive) of the subList
// toIndex high endpoint (exclusive) of the subList
ints.subList(2, 4).clear();
System.out.println(ints);
     输出结果是[0, 1, 4, 5, 6],结果是不是像调用了removeRange(int fromIndex,int toIndex)!效果相同


set(int index, E element) 

set(int index, E element) 用指定的元素替代此列表中指定位置上的元素。

public E set(int index, E element) {
rangeCheck(index);

E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
首先检查范围,用新元素替换旧元素并返回旧元素。


size()

size()返回此列表中的元素数。

public int size() {
//根据modCount判断检查正在读取长度时有没有正在修改数组的操作
checkForComodification();
return this.size;
}

private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}


 toArray() 

 toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。

public Object[] toArray() {        return Arrays.copyOf(elementData, size);
}
    调用Arrays.copyOf将返回一个数组,数组内容是size个elementData的元素,即拷贝elementData从0至size-1位置的元素到新数组并返回。


toArray(T[] a)

toArray(T[] a)按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。

 @SuppressWarnings("unchecked")    public  T[] toArray(T[] a) {
if (a.length // Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
     如果传入数组的长度小于size,返回一个新的数组,大小为size,类型与传入数组相同。所传入数组长度与size相等,则将elementData从0开始到size-1复制到传入数组a中并返回传入的数组。若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。


subList(int fromIndex, int toIndex)

subList(int fromIndex, int toIndex)  是从写了java.util.List接口的方法:

public List subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}

static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex <0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
/* 其中,AbstractList此类提供 List 接口的骨干实现,以最大
限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作
。对于连续的访问数据(如链表),应优先使用 AbstractSequentialList,
而不是此类。 要实现不可修改的列表,编程人员只需扩展此类,并提供
get(int) 和 size() 方法的实现。 要实现可修改的列表,编程人员必须另
外重写 set(int, E) 方法(否则将抛出 UnsupportedOperationException)。
如果列表为可变大小,则编程人员必须另外重写 add(int, E) 和 remove(int) 方法。
*/

private class SubList extends AbstractList implements RandomAccess {
private final AbstractList parent;
private final int parentOffset;
private final int offset;
int size;

SubList(AbstractList parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}

public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}

public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}

public int size() {
checkForComodification();
return this.size;
}

public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}

public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}

protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}

public boolean addAll(Collection c) {
return addAll(this.size, c);
}

public boolean addAll(int index, Collection c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;

checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
//迭代器,实现了这个方法就可成为foreach()的循环对象
public Iterator iterator() {
return listIterator();
}

public ListIterator listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;

return new ListIterator() {
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;

public boolean hasNext() {
return cursor != SubList.this.size;
}

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

public boolean hasPrevious() {
return cursor != 0;
}

@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i <0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[offset + (lastRet = i)];
}

public int nextIndex() {
return cursor;
}

public int previousIndex() {
return cursor - 1;
}

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

try {
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

public void set(E e) {
if (lastRet <0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.set(offset + lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

public void add(E e) {
checkForComodification();

try {
int i = cursor;
SubList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
};
}
      其中,AbstractList此类提供 List 接口的骨干实现,以最大限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作  。对于连续的访问数据(如链表),应优先使用 AbstractSequentialList,而不是此类。 要实现不可修改的列表,编程人员只需扩展此类,并提供 get(int) 和 size() 方法的实现。 要实现可修改的列表,编程人员必须另外重写 set(int, E) 方法(否则将抛出 UnsupportedOperationException)。 如果列表为可变大小,则编程人员必须另外重写 add(int, E) 和 remove(int) 方法。


iterator()

iterator()重写了从java.util.List继承的方法

    public Iterator iterator() {
return new Itr();
}

private class Itr implements Iterator {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
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();
}
}


listIterator()

listIterator()  重写了从java.util.List继承的方法

    public ListIterator listIterator() {
return new ListItr(0);
}

public ListIterator listIterator(int index) {
if (index <0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}

private class ListItr extends Itr implements ListIterator {
ListItr(int index) {
super();
cursor = index;
}

public boolean hasPrevious() {
return cursor != 0;
}

public int nextIndex() {
return cursor;
}

public int previousIndex() {
return cursor - 1;
}

@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i <0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}

public void set(E e) {
if (lastRet <0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

public void add(E e) {
checkForComodification();

try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}

注意:

listIterator()  和iterator() 的区别在于:

iterator() 遍历只能向下遍历,而listIterator() 遍历,不仅可以向下遍历,还可以向上遍历




















推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • Postgresql备份和恢复的方法及命令行操作步骤
    本文介绍了使用Postgresql进行备份和恢复的方法及命令行操作步骤。通过使用pg_dump命令进行备份,pg_restore命令进行恢复,并设置-h localhost选项,可以完成数据的备份和恢复操作。此外,本文还提供了参考链接以获取更多详细信息。 ... [详细]
  • Iamtryingtocreateanarrayofstructinstanceslikethis:我试图创建一个这样的struct实例数组:letinstallers: ... [详细]
  • PHP反射API的功能和用途详解
    本文详细介绍了PHP反射API的功能和用途,包括动态获取信息和调用对象方法的功能,以及自动加载插件、生成文档、扩充PHP语言等用途。通过反射API,可以获取类的元数据,创建类的实例,调用方法,传递参数,动态调用类的静态方法等。PHP反射API是一种内建的OOP技术扩展,通过使用Reflection、ReflectionClass和ReflectionMethod等类,可以帮助我们分析其他类、接口、方法、属性和扩展。 ... [详细]
  • node.jsurlsearchparamsAPI哎哎哎 ... [详细]
author-avatar
大姑氵娘祖茉茉_749
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有