List的子类:
ArrayList:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
允许包括null的所有元素。除了非同步外,该类与Vector相同。
在增加大量元素前通过ensureCapacity操作来增加ArrayList的操作也许能减少重分配的数量。
切记此类是非同步的,如果多个线程同时访问ArrayList的实例且至少有一个在结构上改变了它,那它必须在外部被同步。这一般都是通过对一些自然“包裹”着list的对象进行同步来完成的。如果没有这样的对象,list应当使用Collections.synchronizedList来被“包裹”:
List list = Collections.synchronizedList(new ArrayList(...));
这应当在被创造的时候就完成,以免意外的非同步操作获取该list。
当该类在结构上发生改变时,通过类的iterator()和listIterator(int)返回的迭代器会“立刻出错”(fast-fail),除非这个改变由迭代器自己的ListIterator.remove()或ListIterator.add(object)导致的。因此,当一个ArrayList同时面临修改时,迭代器将立刻且彻底地出错,而非在未来某个不确定的时间产生随机且不确定的风险。
切记,一般而言迭代器的立刻出错行为是不能被保证的,因为当非同步的同时修改发生时制作强大的保证是不可能的。因此不能通过这个异常编写一个正确的程序,它仅能用来检测bug.
ArrayList有4个有意思的字段:
private static final int DEFAULT_CAPACITY = 10; // 默认的初始化容量
private static final Object[] EMPTY_ELEMENTDATA = {}; // 用于空实例的共享空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 用于默认大小空实例的共享空数组
transient Object[] elementData; // 存储ArrayList元素的缓存数组, ArrayList的容量即elementData的长度,
// 空ArrayList的容量将在第一个元素被添加后被扩展到DEFAULT_CAPACITY
ArrayList有3个构造器:
public ArrayList(int initialCapacity); // 当iC == 0时,将EMPTY_ELEMENTDATA赋给elementData, iC > 0时开辟iC大小的Object数组给elementData(此时容量被确定了,但内容是空的),否则抛出异常。
public ArrayList(); // 將DEFAULTCAPACITY_EMPTY_ELEMENTDATA賦给elementData,此时内容为空,容量为10
public ArrayList(Collection<? extends E> c); // 将c的元素数赋给size,若size == 0将EMPTY_ELEMENTDATA赋给elementData,否则将c.toArray()赋给elementData
public void trimToSize(): 最小化ArrayList占有的空间。利用Arrays.copyOf将elementData修剪为size大小,当size == 0时ELEMENTDATA = EMPTY_ELEMENTDTATA。
public void ensureCapacity(int minCapacity): 确保ArrayList至少能包含minCapcity个元素。可能需要调用ensureExplicitCapacity(minCapacity)。
private static int calculateCapacity(Object[] elementData, int minCapacity){
if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
似乎是想说明,当elementData指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,其容量不得小于DEFAULT_CAPACITY。
private void ensureCapcityInternal(int minCapacity){
ensureExplicitCapacity(calculateCapacity(elementDATA, minCapacity));
}
private void ensureExplicitCapcity(int minCapcity){
modCount++;
if(minCapacity - elementData.length > 0)
grow(minCapacity);
}
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);
}
取minCapacity = max(elementData.length*1.5, minCapacity), 将elementData的容量扩充为newCapacity大小,当minCapacity大于MAX_ARRAY_SIZE,即Integer.MAX_VALUE - 8时特殊处理。注意它的size没变。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
对grow()而言,如果所想要扩充的范围很大,hugeCapacity按照minCapacity确定到底给多大。
public int indexOf(Object o): 返回第一个元素o的下标,无则返回-1。通过遍历elementData实现。
public boolean contains(Object o): 判断indexOf(o) >=0?实现。
public int lastIndexOf(Object o): 相比indexOf(),只是将遍历顺序由0~size - 1改为size - 1~0;
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);
}
}
通过创建一个新变量并通过super.clone()初始化,接着将elementData赋给v.elementData。注意clone()在Object.class中以native关键字修饰,说明其方法的实现在其他语言的文件中,因此其过程不得而知。super.clone()可以看成固定用法了,但在涉及深拷贝的时候可能需要以class.clone形式出现。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
通过Array.copyOf()分配新内存,大小根据size确定,而不是elementData.length。
public <T> T[] toArray(T[] a) {
if (a.length < size)
// 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;
}
当参数a的尺寸过小时,开辟新内存返回,否则将elementDATA的size个值拷贝给a,并将第size个赋值为null,以此确定原list的大小。可参考:
public static void main(String[] args) {
String[] ss = {"0", "1", "2", "3", "4"};
List<String> s2 = new ArrayList<>();
s2.add("a");
s2.add("b");
String[] s3 = s2.toArray(ss);
println(ss);
println(s3);
}
/// a b null 3 4
/// a b null 3 4
public static void println(String[] str) {
for(int i = 0; i < str.length; i++){
System.out.print(str[i] + " ");
}
System.out.println();
}
public E get(int index): 先调用rangeCheck(index)检查index是否越界,再返回elementData的第index个元素。
public E set(int index, E element): 同样先检查index是否越界,保存elementData的第index个元素,将其替换并返回。
public boolean add(E e): 调用ensureCapacityInternal(size+1)检查是否越界,再将第size个元素赋值,size++,返回true。
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++;
}
rangeCheckForAdd(index)与rangeCheck(index)的差别在于后者仅在index>size时异常,而前者在index<0是同样会抛出异常(IndexOutOfBoundsException),但是在调用get(-1)时程序会抛出ArrayIndexOutOfBoundsException的异常。第四行意味着把elementData从index开始的size-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;
}
类似于add()。
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;
}
注意判断相等的条件是o.equals(elementData[index]),而equals在object.class中定义为:
public boolean equals(Object obj) {
return (this == obj);
}
即他们的引用相等才为相等,所幸string和long将其重写过,故只要元素相同即可。但自己定义的类则不行。如:
public class Practice {
int var;
public Practice(int v) {
var = v;
}
public static void main(String[] args) {
List<Practice> lm = new ArrayList<>();
lm.add(new Practice(0));
lm.add(new Practice(1));
Practice tmp = new Practice(2);
lm.add(tmp);
lm.add(null);
System.out.println(lm.remove(new Practice(0)));
System.out.println(lm.remove(tmp));
System.out.println(lm.remove(null));
}
}// false true true
private void fastRemove(int index): 与public boolean remove(int index)类似,少了检查和返回。
public void clear(): 将整个size的内容全都指向null,将size归0。
public boolean adAll(Collection<? extends E> c): 先确保容量(size+c.toArray().length),调用System.arraycopy()将c的内容搬运至elementData的size处,改变size。注意其返回值的说明是“list因调用而改变则为true”,事实上返回c.toArray.length!=0。
public boolean addAll(int index, Collection<? extends E> c): 先用rangeCheckForAdd(index)检查越界问题,检查容量。分两次搬运元素,更改size,返回。
protected void removeRange(int fromIndex, int toIndex): 搬运,清除(元素指向null),更改size。
public boolean removeAll(Collection<?> c): 调用Object.requireNull(c)检查c是否为null,是则抛出异常,返回batchRemove(c, false)。
public boolean retainAll(Collection<?> c): 同上,将false改为true。
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
机智!用contains()==complement判断是remove还是retain。但是此步骤可能会抛出异常,所以在finally中判断r != size 的情况下将elementData中r之后的元素都前移留下,同时更改w的大小。之后将w之后的元素赋为null,更改size,返回。
private void writeObject(java.io.ObjectOutputStream s): 将ArrayList的实例状态写到一个流里面,即将其序列化。
public ListIterator<E> listIterator(int index){...}
public ListIterator<E> listInteraor(){...}
public Iterator<E> iterator(){...}
public Spliterator<E> spliterator() {...}
四种生成迭代器的方法。其中第三种返回了一个内部类Itr,其主要的方法是hasNext(),next(),remove()。前两者各返回了一个继承自Itr的内部类ListItr(index/0),它包含方法hasPrevious(), nextIndex(), previousIndex(),set(), add()。注意:若ArrayList内容为("1","2","3"),则next(),next(),previous()输出依次为"1","2","2"。
第四种同样返回了一个内部类ArrayListSpliterator,它继承自Spliterator。它的特点在于在遍历过程中能检查到尽量多的干扰,而不牺牲效率,这主要依赖于modCount。虽然它不能保证检查到并发冲突,对于线程内的干扰也过于保守,但在使用中能检查到足够多的错误,这是值得的。为此,1.惰性初始化fence和expectedModCount;2.在forEach的结尾处只检查一次ConcurrentModificationException。不是很看得懂。
private void readObject(java.io.ObjectInputStream s): 从一个流中重塑ArrayList实例,即反序列化。
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
注意返回list上的改变会导致原list的改变。SubList是ArrayList的一个内部类,可以参考其对外部类变量的引用(如:ArrayList.this.elementData[i]),其重写了set, get等诸多方法,实现方法类似,不赘述。
public void forEach(Consumer<? super E> action): 实现代码不是很能看懂,其效果是将所有元素执行 action动作(但不会改变原有的元素值),除非有异常抛出则终止。在没有明确的规定下,其操作顺序同其迭代顺序。用法为.forEach(k->System.out.println(k));
public boolean removeIf(Predicate<? super E> filter): 创建了一个BitSet存储对应位元素是否需删除。通过遍历元素更新BitSet。再依据BitSet更新elementData,更新size,modCount,返回。用法为.removeIf(k->k=="0");
public void replaceAll(UnaryOperator<E> operator): 遍历所有元素,执行operate操作。用法为.replaceAll(k->k+"ok");
public void sort(Comparator<? super E> c): 调用Array.sort((E[]) elementData, 0 , size, c)。其排序方法各不相同。粗略地看了一下有归并排序,这一部分当成算法学习。