ArrayList源码解析
ArrayList概念标记接口RandomAccessCloneable(Object.clone)浅拷贝深拷贝
java.io.Serializable
源码解析构造函数:扩容添加元素删除元素迭代器:fast-fail快速失败机制Array.asList什么是fail-fast?Vector
ArrayList
概念
数组就是由一块连续的内存组成的数据结构 添加如下:
缺点:
大小固定,不能动态拓展。插入和删除的效率比较慢,假如我们在数组的非尾部插入或删除一个数据,那么就要移动之后的所有数据,这就会带来一定的性能开销,删除的过程如下图所示:
标记接口
先看源码:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess
, Cloneable
, java
.io
.Serializable
实现了三个标记接口:RandomAccess, Cloneable, java.io.Serializable
RandomAccess
支持随机访问(基于下标),为了能够更好地判断集合是ArrayList还是LinkedList,从而能够更好选择更优的遍历方式,提高性能!直接get(i),时间复杂度为O(1)
Cloneable(Object.clone)
支持拷贝:实现Cloneable接口,重写clone方法、方法内容默认调用父类的clone方法。
浅拷贝
基础类型的变量拷贝之后是独立的,不会随着源变量变动而变
String类型拷贝之后也是独立的(final)
引用类型拷贝的是引用地址,拷贝前后的变量引用同一个堆中的对象
public Object
clone() throws CloneNotSupportedException
{
Study s
= (Study
) super.clone();
return s
;
}
其实就是java传值:基本类型传值和引用类型传值
深拷贝
变量的所有引用类型变量(除了String)都需要实现Cloneable(数组可以直接调用clone方法),clone方法中,引用类型需要各 自调用clone,重新赋值
public Object
clone() throws CloneNotSupportedException
{
Study s
= (Study
) super.clone();
s
.setScore(this.score
.clone());
return s
;
}
java的传参,基本类型和引用类型传参 java在方法传递参数时,是将变量复制一份,然后传入方法体去执行。复制的是栈中的内容 所以基本类型是复制的变量名和值,值变了不影响源变量 引用类型复制的是变量名和值(引用地址),对象变了,会影响源变量(引用地址是一样的) String:是不可变对象,重新赋值时,会在常量表新生成字符串(如果已有,直接取他的引用地址),将新字符串的引用地址赋值给栈中的新变量,因此源变量不会受影响
java.io.Serializable
序列化 反序列化:检查serialVersionUID
源码解析
private static final int DEFAULT_CAPACITY
= 10;
private static final Object
[] EMPTY_ELEMENTDATA
= {};
private static final Object
[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA
= {};
transient Object
[] elementData
;
private int size
;
构造函数:
public ArrayList() {
this.elementData
= DEFAULTCAPACITY_EMPTY_ELEMENTDATA
;
}
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
);
}
}
public ArrayList(Collection
<? extends E> c
) {
Object
[] a
= c
.toArray();
if ((size
= a
.length
) != 0) {
if (c
.getClass() == ArrayList
.class) {
elementData
= a
;
} else {
elementData
= Arrays
.copyOf(a
, size
, Object
[].class);
}
} else {
elementData
= EMPTY_ELEMENTDATA
;
}
}
扩容1.5倍,就是将老的数组拷贝一份,老数组就被回收,没有引用地址,elementData指向新的数组
扩容
private void grow(int minCapacity
) {
int oldCapacity
= elementData
.length
;
int newCapacity
= oldCapacity
+ (oldCapacity
>> 1);
if (newCapacity
- minCapacity
< 0)
newCapacity
= minCapacity
;
if (newCapacity
- MAX_ARRAY_SIZE
> 0)
newCapacity
= hugeCapacity(minCapacity
);
elementData
= Arrays
.copyOf(elementData
, newCapacity
);
}
添加元素
默认是尾部添加元素
public boolean add(E e
) {
ensureCapacityInternal(size
+ 1);
elementData
[size
++] = e
;
return true;
}
private void ensureCapacityInternal(int minCapacity
) {
if (elementData
== DEFAULTCAPACITY_EMPTY_ELEMENTDATA
) {
minCapacity
= Math
.max(DEFAULT_CAPACITY
, minCapacity
);
}
ensureExplicitCapacity(minCapacity
);
}
private void ensureExplicitCapacity(int minCapacity
) {
modCount
++;
if (minCapacity
- elementData
.length
> 0)
grow(minCapacity
);
}
public void addEffect(){
int length
= 10000000;
List al
= new ArrayList(length
);
List ll
= new LinkedList();
long start5
= System
.currentTimeMillis();
for(int i
=0;i
<length
;i
++){
al
.add(i
);
}
long end5
= System
.currentTimeMillis();
System
.out
.println(end5
-start5
);
long start6
= System
.currentTimeMillis();
for(int i
=0;i
<length
;i
++){
ll
.add(i
);
}
long end6
= System
.currentTimeMillis();
System
.out
.println(end6
-start6
);
}
所以ArrayList一开始指定初始化容量时,插入的速度不一定会比LinkedList慢 但是 指定下标添加元素,下标越前需要移动的元素就越多,时间复杂度:O(n)
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
++;
}
删除元素
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
;
return oldValue
;
}
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 void fastRemove(int index
) {
modCount
++;
int numMoved
= size
- index
- 1;
if (numMoved
> 0)
System
.arraycopy(elementData
, index
+1, elementData
, index
,numMoved
);
elementData
[--size
] = null
;
}
迭代器:fast-fail快速失败机制
public Iterator
<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor
;
int lastRet
= -1;
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();
}
}
Array.asList
Long
[] arr
=new Long[]{11,23,76,34};
List list
=Array
.asList(arr
);
System
.out
.println(list
.size());
什么是fail-fast?
fail-fast机制是java集合中的一种错误机制。 当使用迭代器迭代时,如果发现集合有修改,则快速失败做出响应,抛出ConcurrentModificationException异常。 这种修改有可能是其它线程的修改,也有可能是当前线程自己的修改导致的,比如迭代的过程中直接调用remove()删除元素等。 另外,并不是java中所有的集合都有fail-fast的机制。比如,像最终一致性的ConcurrentHashMap、CopyOnWriterArrayList等都是没有fast-fail的。fail-fast是怎么实现的: ArrayList、HashMap中都有一个属性叫modCount,每次对集合的修改这个值都会加1,在遍历前记录这个值到expectedModCount中,遍历中检查两者是否一致,如果出现不一致就说明有修改,则抛出ConcurrentModificationException异常。 底层数组存/取元素效率非常的高(get/set),时间复杂度是O(1),而查找(比如:indexOf,contain),插入和删除元素效率不太高,时间复杂度为O(n)。 插入/删除元素会触发底层数组频繁拷贝,效率不高,还会造成内存空间的浪费,解决方案:linkedList 查找元素效率不高,解决方案:HashMap(红黑树)
Vector
少了空对象 扩容因子2 只是synchronize锁了方法,不是锁全局synchronized(this),如果多个线程操作的是多个方法,还是锁不住它,所以Vector不一定线程安全。