特性 实现了三个标记接口: RandomAccess, Cloneable, java.io.Serializable
public class ArrayList <E > extends AbstractList <E > implements List <E >, RandomAccess , Cloneable , java .io .Serializable
RandomAccess 支持随机访问(基于下标),为了能够更好地判断集合是ArrayList还是LinkedList,从而能够更好选择更优的遍历方式,提高性能!
Cloneable 支持拷贝:实现Cloneable接口,重写clone方法、方法内容默认调用父类的clone方法。 1.浅拷贝 对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝
public Object clone () throws CloneNotSupportedException { Study study = (Study) super .clone(); return study; }
2.深拷贝 对基本数据类型进行值传递,对引用数据类型创建一个新的对象,并复制内容,这是深拷贝
public Object clone () throws CloneNotSupportedException { Study s = (Study) super .clone(); s.setScore(this .score.clone()); return s; }
java的传参有基本类型和引用类型传参,参数传递时拷贝的都是栈中的内容。此处大概分为三种情况
1.基本类型,存储在栈中所以拷贝的就是真实的值,修改后不影响原值 2.引用类型:栈中内容为对象引用,拷贝的也为引用 修改改变的是引用所指向的对象,由于引用的同一个对象,所以元对象改变了 3.String:虽然String也是引用类型 但于String是不可变对象 在修改时会讲引用指向一个新的对象,所已他们的引用变得不同了,当然不会改变原值
Serializable 序列化:将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据,在Java中的这个Serializable接口其实是给jvm看的,通知jvm,我不对这个类做序列化了,你(jvm)帮我序列化就好了。如果我们没有自己声明一个serialVersionUID变量,接口会默认生成一个serialVersionUID,默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常(每次序列化都会重新计算该值)
AbstractList 继承了AbstractList ,说明它是一个列表,拥有相应的增,删,查,改等功能。
List 为什么继承了 AbstractList 还需要 实现List 接口?
1、在StackOverFlow 中:传送门 得票最高的答案的回答者说他问了当初写这段代码的 Josh Bloch,得知这就是一个写法错误。 I’ve asked Josh Bloch, and he informs me that it was a mistake.He used to think, long ago, that there was some value in it, but he since “saw the light”.Clearly JDK maintainers haven’t considered this to be worth backing out later.
基本属性 private static final long serialVersionUID = 8683452581122892189L ;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; } }
添加元素 尾部插入(默认) 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); }
插入性能与linkedList对比
package jdk8.list;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;public class EffectTest { public static void main (String[] args) { int length = 10000000 ; List arrayList = new ArrayList(length); List linkedList = new LinkedList(); long start5 = System.currentTimeMillis(); for (int i=0 ;i <length;i++){ arrayList.add(i); } long end5 = System.currentTimeMillis(); System.out.println("arrayList:" +(end5-start5)); long start6 = System.currentTimeMillis(); for (int i=0 ;i <length;i++){ linkedList.add(i); } long end6 = System.currentTimeMillis(); System.out.println("linkedList:" +(end6-start6)); } }
arrayList涉及扩容,会消耗性能但是如果提前指定容量,会提升性能,可以达到与linkedList相当,甚至超越
指定下标插入 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++; }
时间复杂度为O(n),与移动的元素个数正相关
扩容 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); } private static int hugeCapacity (int minCapacity) { if (minCapacity < 0 ) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_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 ; }
迭代器 iterator 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(); } }
remove 方法的弊端。 1、只能进行remove操作,add、clear 等 Itr 中没有。 2、调用 remove 之前必须先调用 next。因为 remove 开始就对 lastRet 做了校验。而 lastRet 初始化时为 -1。 3、next 之后只可以调用一次 remove。因为 remove 会将 lastRet 重新初始化为 -1
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(红黑树)
注意事项 1.创建时指定最好初始长度,减少扩容次数提升效率 2.arrayList.subList(beginIndex,endIndex) 返回其内部类 通过指定偏移量操作原对象。(源对象不可变此处有fast-fail检查) 3.属性 modCount 每次添加删除元素 +1 fast-fail 检查 4.List unmodifiableList = Collections.unmodifiableList(list); 不可变list 原对象可变
UnmodifiableList(List<? extends E> list) { super (list); this .list = list; } public E get (int index) {return list.get(index);}public E set (int index, E element) { throw new UnsupportedOperationException(); } public void add (int index, E element) { throw new UnsupportedOperationException(); } public E remove (int index) { throw new UnsupportedOperationException(); }
5.Arrays.asList(T..) 返回的是Arrays内部类。若传入的数组为基本类型,返回的list长度一直为1。
Integer[] array = {1 ,2 ,3 }; List list = Arrays.asList(array); int [] array1 = {1 ,2 ,3 };List list1 = Arrays.asList(array1);
6.List的最大长度为什么为 Integer.max-8