ArrayList
是我们常用的一种数据结构。仔细分析 ArrayList
这个类和类所包含的方法。
ArrayList继承了AbstractList 和使用了List RandomAccess Cloneable和Serializable四个接口。
AbstractList
该接口的作用是用于 AbstractList
提供了 List
接口的默认实现,在AbstractList
中已经使用了List
接口,为什么还会在ArrayList
中使用的List
接口,目测是为了让大家更加明白清楚的知道这个类是List这个集合。
RandomAccess
是
ArrayList
只有两个私有变量,分别是size
和elementData
。
elementData
这个是来记录传入ArrayList
的元素。而size
是用来记录传入的元素的个数。
该类里面一共有三个构造器
- ArrayList()
- ArrayList(int)
- ArrayList(Collection)
下面就详细的分析一下三个构造器的使用。
ArrayList无参的构造器,使用这个无参构造器会默认有一个长度为10的数组。
1
2
3
| public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
|
传入一个int值,这个值为ArrayList
的初始容量。如果等于0则会默认使用EMPTY_ELEMENTDATA
生成一个空的集合。如果输入为非负数的话会抛出一个IllegalArgumentException
(非法参数)异常。
1
2
3
4
5
6
7
8
9
10
| 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);
}
}
|
这个构造函数是传入一个集合,toArray
是将这个集合转化为一个实际的数组,下面的程序就是和传入int
类型是差不多的,判断数组长度是否为0,如果等于0则会默认使用EMPTY_ELEMENTDATA
生成一个空数组。在不为空的时候,将Collection
的值copy到ArrayList
。
1
2
3
4
5
6
7
8
9
10
11
| 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
实际容量调整为列表当前大小。这里它实用了一个三目运算**? :
**。
modCount
这个变量在AbstractList
这个类里面,其中定义int
这个变量使用了transient
。
protected transient int modCount = 0;
transient
当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中。
- 三目运算和
if else
的区别,三目运算是会有一个返回值的,if else
没有返回值。如果非要写成if else
应该也可以。
1
2
3
4
5
6
7
8
| public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
|
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
DEFAULT_CAPACITY=10是一个常量
1
2
3
4
5
6
7
8
9
10
11
| public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
|
返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。更确切地讲,返回满足 (o==null ? get(i)==null : o.equals(get(i))) 的最低索引 i ,如果不存在此类索引,则返回 -1。
1
2
3
4
5
6
7
8
9
10
11
12
| public int indexOf(Object o) {
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;
}
return -1;
}
|
如果此列表中包含指定的元素,则返回 true。更确切地讲,当且仅当此列表包含至少一个满足 (o==null ? e==null : o.equals(e)) 的元素 e 时,则返回 true。
1
2
3
| public boolean contains(Object o) {
return indexOf(o) >= 0;
}
|
lastIndexOf
用于返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
这里if
和else
里面采用了两种不同的比较方法。if
代码块里面是采用了==
说明比较的是对象,而else
的代码块采用的是equals
比较。为什么这里采用两种比较方法呢?
这里产生一个疑问?
null
是对象还是值?
正在技术论坛上提问
Keywords
50 character sequences, formed from ASCII letters, are reserved for use as keywords and cannot be used as identifiers (§3.8).
Keyword: one of
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
The keywords const and goto are reserved, even though they are not currently used. This may allow a Java compiler to produce better error messages if these C++ keywords incorrectly appear in programs.
While true and false might appear to be keywords, they are technically Boolean literals (§3.10.3). Similarly, while null might appear to be a keyword, it is technically the null literal (§3.10.7).
在这个50个关键字中没有null
,但是下面的一行字写了。
While true and false might appear to be keywords, they are technically Boolean literals (§3.10.3). Similarly, while null might appear to be a keyword, it is technically the null literal (§3.10.7).
虽然true
和false
可能看起来是关键字,但它们在技术上是布尔值(§3.10.3)。类似地,虽然null
可能看起来是一个关键字,但在技术上是null
值(§3.10.7)。
java的官方文档里写着true
,false
,null
是一个值,也就是说java有50个关键字,3个特殊的值。
竟然是值,为什么在比较的时候会报错?
1
2
3
4
5
6
7
8
| public class Main {
public static void main(String[] args) {
String a = null;
String b = null;
System.out.println(a == b);
System.out.println(a.equals(b));
}
}
|
输出结果
1
2
3
4
5
6
7
8
9
10
| true
Exception in thread "main" java.lang.NullPointerException
at Main.Main.main(Main.java:14)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Process finished with exit code 1
|
第二个方法抛出一个空指针异常。
竟然是一个值,那么应该两种都会显示ture
。
这样也就说明了为什么和null
比较时要用==
而不是equals
。
1
2
3
4
5
6
7
8
9
10
11
| 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;
}
|
返回此 ArrayList 实例的浅表复制。(不复制这些元素本身。)
什么是浅表复制(shallow copy)
对于shallow copy的翻译很多,有叫浅表复制,浅复制,影子复制,与它向对的是深度复制。
浅表复制
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| /**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
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(e);
}
}
|
该方法是用于添加将指定元素添加到末尾,通过ensureCapacityInternal()
这个方法来为ArrayList扩容,在为elementData
的末尾添加指定元素。代码的注释也强调了,是增加modCount
。
1
2
3
4
5
| public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
|
这个方法的一开始就调用了rangCheckForAdd()
这个方法,这个私有方法主要是用来判断传入的索引,如果传入的数值大于最大值或者小于零会抛出一个越界异常。System.arraycopy()
是用来复制数组的,将要添加位置之后的数组整体后移一位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| 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));
}
|
移除此列表中首次出现的指定元素(如果存在)。如果列表不包含此元素,则列表不做改动。更确切地讲,移除满足 (o==null ? get(i)==null : o.equals(get(i))) 的最低索引的元素(如果存在此类元素)。如果列表中包含指定的元素,则返回 true(或者等同于这种情况:如果列表由于调用而发生更改,则返回 true)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| 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;
}
|
通过一个循环将ArrayList
中的元素赋值为null
,最后将size
赋值为0,这样应该就会保证没有浪费内存。
1
2
3
4
5
6
7
8
9
| public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
|
用于集合遍历,如果索引大于最大值或者小于0,抛出异常,否者使用ListItr()进行。
1
2
3
4
5
| public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
|
这是一个私有的内部类,继承了Iterator这个接口,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| 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;
}
@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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
|
这个方法是用来替换所有的匹配项
传入的参数是UnaryOperator
是在java 8引入的lambda表达式,具体如何使用以后再说。
1
2
3
4
5
6
7
8
9
10
11
12
| public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
|
1
2
3
4
5
6
7
8
| public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
|