Java8集合框架——ArrayList源码分析
java.util.ArrayList
以下为主要介绍要点,从 Java 8 出发:
- 一、ArrayList的特点概述
- 二、ArrayList的内部实现:从内部属性和构造函数说起
- 三、ArrayList添加元素和扩容
- 四、ArrayList删除元素
- 五、ArrayList查找和修改元素
- 六、ArrayList的遍历和出现的问题
- 七、ArrayList的内部元素elementData为何用transient修饰
- 八、ArrayList和Vector的比较
- 九、Java8中ArrayList的部分改动说明
一、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 的大小。
另外还有指定索引添加元素的,代码如下。总结来说大致步骤如下:
- 判断边界
- 扩容
- 从指定索引处开始的元素都往后移动一位
- 插入指定索引的指定元素,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类:
- 指定索引删除元素
- 指定元素删除:这里找到第一个equals或者null(如元素为null)即可
1. 指定索引删除元素
- 边界判断
- modCount++
- 记录指定索引的旧元素
- 非最后一个元素,即size - index - 1 > 0,把元素前移一个单位。
- 清空最后一个索引元素
- 返回旧元素
源码如下。这里边界检查只做了 >= 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 ? get(i)==null : 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本身实现了Cloneable和Serializable,但是关键的成员变量却是用了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源码分析的更多相关文章
- Java8集合框架——LinkedList源码分析
java.util.LinkedList 本文的主要目录结构: 一.LinkedList的特点及与ArrayList的比较 二.LinkedList的内部实现 三.LinkedList添加元素 四.L ...
- Java基础-集合框架-ArrayList源码分析
一.JDK中ArrayList是如何实现的 1.先看下ArrayList从上而下的层次图: 说明: 从图中可以看出,ArrayList只是最下层的实现类,集合的规则和扩展都是AbstractList. ...
- Java8集合框架——LinkedHashMap源码分析
本文的结构如下: 一.LinkedHashMap 的 Javadoc 文档注释和简要说明 二.LinkedHashMap 的内部实现:一些扩展属性和构造函数 三.LinkedHashMap 的 put ...
- Java8集合框架——HashMap源码分析
java.util.HashMap 本文目录: 一.HashMap 的特点概述和说明 二.HashMap 的内部实现:从内部属性和构造函数说起 三.HashMap 的 put 操作 四.HashMap ...
- Java8集合框架——LinkedHashSet源码分析
本文的目录结构如下: 一.LinkedHashSet 的 Javadoc 文档注释和简要说明 二.LinkedHashSet 的内部实现:构造函数 三.LinkedHashSet 的 add 操作和 ...
- Java8集合框架——HashSet源码分析
本文的目录结构: 一.HashSet 的 Javadoc 文档注释和简要说明 二.HashSet 的内部实现:内部属性和构造函数 三.HashSet 的 add 操作和扩容 四.HashSet 的 r ...
- Java集合干货——ArrayList源码分析
ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...
- Java集合之ArrayList源码分析
1.简介 List在数据结构中表现为是线性表的方式,其元素以线性方式存储,集合中允许存放重复的对象,List接口主要的实现类有ArrayList和LinkedList.Java中分别提供了这两种结构的 ...
- 【Java集合】ArrayList源码分析
ArrayList是日常开发中经常使用到的集合,其底层采用数组实现,因此元素按序存放.其优点是可以使用下标来访问元素,时间复杂度是O(1).其缺点是删除和增加操作需要使用System.arraycop ...
随机推荐
- epoll源码分析(基于linux-5.1.4)
API epoll提供给用户进程的接口有如下四个,本文基于linux-5.1.4源码详细分析每个API具体做了啥工作,通过UML时序图理清内核内部的函数调用关系. int epoll_create1( ...
- bzoj 4652: [Noi2016]循环之美
额,,网上一堆题解,,随便一找就找到笨蒟蒻扒的了. 这个比较神奇的是纯循环小数就是[(y,k)=1],题解有证明这个的,貌似就是k进制下的类似循环节,不会不会.. 然后这道题就变成了求这个东西:∑(x ...
- python 关于异常处理 try...except... 的两个案例
输入若干个成绩,求所有成绩的平均分.每输入一个成绩后询问是否继续输入下一个成绩,回答“yes”就继续输入下一个成绩,回答“no”就停止输入成绩. numbers = [] #使用列表存放临时数据 wh ...
- NO4 find&mv-&-特殊符号..和.
问题七:退到上一级目录,删除data目录. 解答:cd ..或cd ../ rm -r data或rmdir data#空目录就不需要带-rf,杀鸡不用宰牛刀,rmdir基本要淘汰的命令 ...
- CodeForces - 755C PolandBall and Forest (并查集)
题意:给定n个数,Ai的下标为1~n.对于每一个i,Ai与i在同一个树上,且是与i最远的点中id最小的点(这个条件变相的说明i与Ai连通).求森林中树的个数. 分析:若i与Ai连通,则在同一个树上,因 ...
- C++路径的整理
写C++,路径的问题一直都让人很头疼,抽空整理一些方法:也许以后会用到: 1."./" 加不加都一样,就是指当前目录 2."../" 表示当前目录的上级目录,即 ...
- jQuery实现图片放大镜效果
实现图片放大镜的原理: 给放大镜元素一个对应的html元素为<div class='right'> 设置这个div的宽高固定为某个值(350px,350px) 设置div的css为超出部分 ...
- maven手动安装ojdbc6.jar包到本地仓库
需要jar文件 ojdbc6.jar jar下载地址1 下载地址2 本地执行: mvn install:install-file -Dfile=D:/ojdbc6.jar -DgroupId=co ...
- 解决dispatch 后在当前页面不能获取最新的this.props中的数据
因为dispatch为异步方法 .解决这个问题方法很多 出去添加定时器这种不是很优雅的方法 我们可以用 componentWillReceiveProps(nextProps) 这个生命周期 ...
- Django——整体结构/MVT设计模式
MVT设计模式 Models 封装数据库,对数据进行增删改查; Views 进行业务逻辑的处理 Templates 进行前端展示 前端展示看到的是模板 --> 发起 ...