java.util.ArrayList

以下为主要介绍要点,从 Java 8 出发:

一、ArrayList的特点概述

  从ArrayList本身特点出发,结论如下:

关注点 ArrayList相关结论

是否允许空的元素

是否允许重复的元素

元素有序:读取数据和存放数据的顺序一致

是否线程安全
随机访问的效率 随机访问指定索引(即数组的索引)的元素快
顺序添加元素的效率

在不涉及扩容时,顺序添加元素速度快;

当需要扩容时,涉及到元素的复制,相对较慢

删除和插入元素的效率

因涉及到复制和移动后续的元素,相对较慢

二、ArrayList的内部实现:从内部属性和构造函数说起

  ArrayList是一个内部以数组方式实现列表、可以自动扩容的集合。其内部实现有5个重要的属性,源码如下:

    /**
* Default initial capacity.
* 默认的初始化元素个数(容量),使用ArrayList()创建时(即不指定容量),首次添加元素会进行
* 内部数组的首次扩容,扩容容量就是DEFAULT_CAPACITY = 10
*/
private static final int DEFAULT_CAPACITY = 10; /**
* Shared empty array instance used for empty instances.
* 用于构造空实例时的默认共享空数组,在使用 ArrayList(0) (即指定容量为0)或者
* ArrayList(Collection<? extends E> c)且c.size()=0 (即使用空集合来创建),
* 就会使用该空数组作为默认的空实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {}; /**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 另一个共享的空数组,使用ArrayList()时默认的空实现,区别于上面的EMPTY_ELEMENTDATA,
* 用来判断添加第一个元素时是否需要按照默认的容量DEFAULT_CAPACITY进行扩容.
* 其他有指定初始容量的ArrayList(即便大小是0),涉及到的扩容便按照默认的规则进行
*/
private static final Object[] DEFAULTCAPACITY_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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* 实际存储列表元素的数组。这也是读取数据和存放数据的顺序一致、随机访问指定元素、
* 顺序添加元素快(在末尾添加,且不涉及扩容的情况下)的原因。
*/
transient Object[] elementData; // non-private to simplify nested class access /**
* The size of the ArrayList (the number of elements it contains).
* 数组中元素的实际个数
* @serial
*/
private int size; /**
* 用于记录被修改(增加/删除/修改等)的次数
*/
protected transient int modCount = 0;

  ArrayList有3个常规的构造函数。

1. 空参构造函数

  直接使用共享空数组:DEFAULTCAPACITY_EMPTY_ELEMENTDATA 。

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

  这里注释写着构造一个初始容量为10的空数组??? 其实意思是说,通过这个构造函数建立的ArrayList,初始容量都是10,而初始容量则是在第1次添加元素时进行扩容的。后续的添加元素的源码中,可以看到正是通过  elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA  来将默认容量设置为10的。

2. 指定初始容量的构造函数

  比较简单的按照初始容量构造内部数组,或者是默认的共享空数组(指定初始容量为0时) EMPTY_ELEMENTDATA

    /**
* 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) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}

3. 通过指定的集合进行构造

  同样当指定集合的大小为0时,也会默认为共享空数组 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<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}

三、ArrayList添加元素和扩容

源码如下:

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

  add(E e) 调用了 ensureCapacityInternal(size + 1) 来判断容量是否满足,首先判断是否是默认的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,若是则说明创建的时候没有指定容量,此时按照默认容量DEFAULT_CAPACITY = 10 进行首次扩容(size + 1 = 1)。而 ensureCapacityInternal(size + 1)调用了ensureExplicitCapacity(minCapacity) 再一次进行判断,这个方法记录了总共进行修改过的次数modCount,同时进行了实际的数组扩容。当然只有实际数组元素个数size超过数组长度时才会进行扩容。

  从这里可以看到,如果原先有指定初始容量,那么后续的扩容都按照原始的容量来进行的,与默认容量10就没有关系了。

    // 没有指定初始容量时,按照默认的 DEFAULT_CAPACITY 进行扩容
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 实际超过数组长度才会进行扩容
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

  扩容的方法如下。对于没有溢出的,实际的扩容是原来的1.5倍,这里使用了位运算,右移一位类似与/2操作,取了一半,但是位操作快。这里需要说明的是,默认空构造器时建立的ArrayList也是在这里首次进行扩容的,使用默认容量 DEFAULT_CAPACITY = 10

  这里扩容后还需要将原来的元素复制到新的位置中,因此说涉及到的扩容的改动操作都会比较耗时。

    /**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
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);
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;
}

  elementData[size++] = e;就是直接的元素赋值,然后增加 size 的大小。

  另外还有指定索引添加元素的,代码如下。总结来说大致步骤如下:

  1. 判断边界
  2. 扩容
  3. 从指定索引处开始的元素都往后移动一位
  4. 插入指定索引的指定元素,size加1,完成。

  从这里可以看出,因涉及到复制和移动其他的元素,插入元素比较慢。删除也是类似的。

    /**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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++;
}

  从这两处源码可以看到,ArrayList并没有判断元素是什么,而是直接存储了,因此说:ArrayList允许空的或者重复的元素。

四、ArrayList删除元素

删除元素有2类:

  1. 指定索引删除元素
  2. 指定元素删除:这里找到第一个equals或者null(如元素为null)即可

1. 指定索引删除元素

  1. 边界判断
  2. modCount++
  3. 记录指定索引的旧元素
  4. 非最后一个元素,即size - index - 1 > 0,把元素前移一个单位。
  5. 清空最后一个索引元素
  6. 返回旧元素

源码如下。这里边界检查只做了 >= size,因为数组元素本身不会超过size的,而使用 < 0 的index时,对于elementData(index),本身就是IndexOutOfBound的,并不需要直接判断。

    /**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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;
}
/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

2. 指定元素删除

  删除的步骤类似,只是这里是遍历找到第一个指定的元素而已,然后同样需要进行后面元素的前迁。

    /**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
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;
} /*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
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
}

五、ArrayList查找和修改元素

1. 按照指定索引查找元素

  这里只做了边界检查,如果没有越界,直接返回指定索引的元素即可,因此说速度比较快。

    /**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
rangeCheck(index); return elementData(index);
}

 2. 按照指定索引修改元素

  指定索引修改元素,按照索引,速度也是比较快。

    /**
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
rangeCheck(index); E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}

六、ArrayList的遍历和出现的问题

  几种遍历方式:for循环(按照索引下标)、迭代器遍历、forEach遍历。

1、for循环(按照索引下标)

  这里就是简单地通过 get(i) 来获取,略。。。

2、迭代器遍历 Iterator

  Iterator 是各种集合类中比较标准的访问方式,它隐藏了各种集合的内部结构,抽象出统一的访问方式。迭代器本身3个比较重要的接口如下:

    /**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext(); /**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next(); /**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
* only once per call to {@link #next}. The behavior of an iterator
* is unspecified if the underlying collection is modified while the
* iteration is in progress in any way other than by calling this
* method.
*
* @implSpec
* The default implementation throws an instance of
* {@link UnsupportedOperationException} and performs no other action.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this iterator
*
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}

  具体到ArrayList,调用其 iterator() 方法,返回的则是内部类 Itr 。

    /**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}

  下面看看 Itr 如何处理上面贴出来的3个接口的。

  首先默认 cursor = 0,从索引 0 处进行元素的遍历没有问题。而元素的最大索引是 size - 1,因此在 hasNext()  中通过 cursor != size 来判断是否还有下一个元素或者说遍历结束。另外这里还记录了modCount,在后续中会通过此变量的变化来判断在遍历过程中,当前集合是否被修改过,从而抛出 ConcurrentModificationException (fail-fast机制)。

    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 boolean hasNext() {
return cursor != size;// size表示数组实际元素数量,cursor == size表示已经遍历结束
}
}

  接下来看看 next() 方法,它返回当前需要遍历的元素,如果在调用前当前集合被修改过而且之前记录的 expectedModCount 没有被修改过,也即就是 modCount != expectedModCount,则会抛出 ConcurrentModificationException 。

    @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移动1位
cursor = i + 1;
// 返回实际的索引,同时记录已返回的下标lastRet
return (E) elementData[lastRet = i];
}
// 检查在遍历过程中,当前集合是否被修改过
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

  而当中只有调用 remove() 才会重新对expectedModCount进行更新,而且也只有调用这里的 remove 进行元素的移除才能保证安全。

    public void remove() {
// 没有先调用next()而先调用remove()明显是不行的,此时lastRet = -1会直接抛出异常
if (lastRet < 0)
throw new IllegalStateException();
// 检查当前集合是否被修改过
checkForComodification(); try {
// 实际上还是调用ArrayList本身的remove
ArrayList.this.remove(lastRet);
// 当前集合已经移除了一个元素,对crusor进行复位
cursor = lastRet;
// lastRet进行复位,确保下一次remove前判断有效
lastRet = -1;
// 重新记录modCount
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

3、forEach遍历

  这里 forEach 也只是遍历获取elementData[i] ,当然内部会有一些判断,确保不会越界而且是否会有 ConcurrentModificationException 异常,有一定地安全性。

    @Override
public void forEach(Consumer<? super E> action) {
// 例行地非空判断
Objects.requireNonNull(action);
// 记录modCount,同时使用final标识说明不允许修改
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
// 每次获取元素前都需要判断是否索引正常/modCount正常
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}

4、遍历出现的问题

  在 for 循环中遍历集合的时候使用 remove 会有什么问题呢?例子如下,例子1 中由于有实时判断边界,因此并没有出错,而例子2 由于中途进行了 remove 操作而导致 List 的 size 发生变化了,而原来记录的 size 并没有进行更新,再次进行 remove ,rangeCheck便可检测出越界异常。

    // 例子1:没有报错
List<String> strList1 = new ArrayList<>();
strList1.add("1");
strList1.add("2");
strList1.add("3");
strList1.add("3");
for (int i = 0; i < strList1.size(); i++) {
if ("3".equals(strList1.get(i))) {
strList1.remove(i); // remove之后,i++为3,此时strList1.size()也为3,正确退出不出错
}
}
System.out.println(strList1); // 例子2:Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
List<String> strList2 = new ArrayList<>();
strList2.add("1");
strList2.add("2");
strList2.add("3");
strList2.add("3");
int size = strList2.size(); //
for (int i = 0; i < size; i++) {
if ("3".equals(strList2.get(i))) {
strList2.remove(i); // remove之后,i = 2,而size仍为4,i++为3,仍然满足条件,而此时就越界了
}
}
System.out.println(strList2);

5、ConcurrentModificationException异常

  迭代器中多次提到了 ConcurrentModificationException 异常,当然也只有在 modCount 和 expectedModCount 不一致时才会这样。那什么时候会出现不一致呢:使用集合的 add 或者 remove 就会改变 modCount ,制造出机会。多线程或者单线程下进行模拟操作都可以,下面举个单线程例子:

    List<String> itrList = new ArrayList<>();
itrList.add("1");
itrList.add("2");
itrList.add("3");
itrList.add("4");
Iterator<String> iterator = itrList.iterator();
while (iterator.hasNext()) {
String nextStr = iterator.next();
if ("3".equals(nextStr)) {
// 操作1:正常不会报错???
// itrList.remove("3");
// 操作2:正常不会报错
iterator.remove();
}
if ("2".equals(nextStr)) {
// 操作3:Exception in thread "main" java.util.ConcurrentModificationException
// itrList.remove("2");
}
}
System.out.println(itrList);

  其中操作2没有抛出异常可以理解,毕竟 iterator 本身的 remove 会考虑到 expectedModCount 的修正。但是操作1和操作3同样是使用集合本身的 remove,但是操作3如期抛出了异常,而操作1并没有。其实,操作1中进行remove之后,iterator 内部的 cursor = 3,且 cursor == size,此时 iterator.hasNext() 中 cursor != size 返回 false,因此退出了,而操作3则还会继续,因此 checkForComodification 时便可检查出 ConcurrentModificationException。更多可参考 :Java ConcurrentModificationException异常原因和解决方法

6、ConcurrentModificationException异常的一道面试题

  网上copy过来的1道题,这里抛出来的异常是 IndexOutOfBoundsException 而并非 ConcurrentModificationException。

  其中的 testList.iterator().hasNext() 每次都是返回一个新的 iteraror ,在进行 testList.remove 之后,新的modCount 便赋给了新的 iteraror,而且也没有对新的 iteraror 进行什么操作;而 i++ 却逐渐累计,testList.size() 逐渐变小,当进行到 i = 5 时,实际上 size = 5,此时 testList.remove(5) 便抛出了如期的 IndexOutOfBoundsException。

    ArrayList<String> testList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
testList.add("sh" + i);
}
for (int i = 0; testList.iterator().hasNext(); i++) {
testList.remove(i);
System.out.println("test" + testList.get(i));
}
// Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 5, Size: 5

七、ArrayList的内部元素elementData为何用transient修饰

  ArrayList本身实现了CloneableSerializable,但是关键的成员变量却是用了transient进行了修饰,不希望被序列化。

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

  从序列化的实现来看,只对elementData中有元素的部分进行了序列化,并没有全部元素,这是合理的,一般elementData的容量比实际的size大,没有必要所有元素都行序列化。这也提高了时间效率,同时节省了空间。

     /**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size); // Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
} if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}

 八、ArrayList和Vector的比较

1、Vector是线程安全的:Vector大部分方法都和ArrayList差不多,但是其实现都添加了synchronized来保证操作的安全性。

2、Vector可以指定扩容的增长因子capacityIncrement,每次需要扩容时会根据扩容因子进行判断,直接扩展指定的因子,或者是倍增,如下:

    private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
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;
}

九、Java8中ArrayList的部分改动说明

  相比Java7,Java8中增加了 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 并作为默认空构造器时的空实现,而原来的 EMPTY_ELEMENTDATA 则改为在 指定容量且容量为0 或者 指定初始化的集合而集合大小也为0 时的空实现。

     /**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {}; /**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

Java8集合框架——ArrayList源码分析的更多相关文章

  1. Java8集合框架——LinkedList源码分析

    java.util.LinkedList 本文的主要目录结构: 一.LinkedList的特点及与ArrayList的比较 二.LinkedList的内部实现 三.LinkedList添加元素 四.L ...

  2. Java基础-集合框架-ArrayList源码分析

    一.JDK中ArrayList是如何实现的 1.先看下ArrayList从上而下的层次图: 说明: 从图中可以看出,ArrayList只是最下层的实现类,集合的规则和扩展都是AbstractList. ...

  3. Java8集合框架——LinkedHashMap源码分析

    本文的结构如下: 一.LinkedHashMap 的 Javadoc 文档注释和简要说明 二.LinkedHashMap 的内部实现:一些扩展属性和构造函数 三.LinkedHashMap 的 put ...

  4. Java8集合框架——HashMap源码分析

    java.util.HashMap 本文目录: 一.HashMap 的特点概述和说明 二.HashMap 的内部实现:从内部属性和构造函数说起 三.HashMap 的 put 操作 四.HashMap ...

  5. Java8集合框架——LinkedHashSet源码分析

    本文的目录结构如下: 一.LinkedHashSet 的 Javadoc 文档注释和简要说明 二.LinkedHashSet 的内部实现:构造函数 三.LinkedHashSet 的 add 操作和 ...

  6. Java8集合框架——HashSet源码分析

    本文的目录结构: 一.HashSet 的 Javadoc 文档注释和简要说明 二.HashSet 的内部实现:内部属性和构造函数 三.HashSet 的 add 操作和扩容 四.HashSet 的 r ...

  7. Java集合干货——ArrayList源码分析

    ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...

  8. Java集合之ArrayList源码分析

    1.简介 List在数据结构中表现为是线性表的方式,其元素以线性方式存储,集合中允许存放重复的对象,List接口主要的实现类有ArrayList和LinkedList.Java中分别提供了这两种结构的 ...

  9. 【Java集合】ArrayList源码分析

    ArrayList是日常开发中经常使用到的集合,其底层采用数组实现,因此元素按序存放.其优点是可以使用下标来访问元素,时间复杂度是O(1).其缺点是删除和增加操作需要使用System.arraycop ...

随机推荐

  1. Day6-T4

    原题目 Describe:差分约束模板题吧...LG上竟然是省选+ code: #include<bits/stdc++.h> #define INF 214748364 using na ...

  2. imp.load_source的用法

    imp.load_source(name,pathname[,file])的作用把源文件pathname导入到name模块中,name可以是自定义的名字或者内置的模块名称. 假设在路径E:/Code/ ...

  3. 024、Java中字符串连接字符串拼接

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  4. DOM:通过文本框向下拉列表中添加内容

    <!DOCTYPE html><html> <head>        <meta charset="UTF-8">         ...

  5. Most simple basic of internet programming based on two different machines sharing the same local net

    This blog is just shown for the most simple basic of internet programming based on two different mac ...

  6. SQL server 注入 和 SQL server 扩展(10.29 第二十九天)

    Step1:检测注入点 Step2: select * from sysobjects   (sysobjects 系统对象表,保存当前数据库的对象) select * from users wher ...

  7. Python 列表/元组/字典总结

    序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内置类型,但最常见的是列表和元组. 序列 ...

  8. 八十八、SAP中ALV事件之二,事件的定义和事件子例程

    一.我们来到SE37,找到REUSE_ALV_EVENTS_GET相关的定义 二.我们需要用到下面这3个事件 三.我们添加一个第五步,并把显示ALV座位第六步.在第五步中定义三个事件 四.在末尾,添加 ...

  9. 114-PHP判断类变量是否相同

    <?php class ren{ //定义人类 } class mao{ //定义人类 } $ren=new ren(); //实例化人类的对象 $ren_a=new ren(); //实例化人 ...

  10. lastz

    lastz sequence1.fasta sequence2.fasta 其中,sequence1.fasta是reference genome :sequence2.fasta是需要比对的geno ...