集合源码学习(三):ArrayList
2017-10-11 00:09
363 查看
Java集合里面最简单的估计就是
jdk注释为:
从定义头的代码可以看出,ArrayList继承自AbstractList,支持泛型,可以随机存取,可以克隆,可以序列化都在代码中有所体现。
如果范围不够了,自动增长长度将为原长度的1.5倍
当然如果主动要求扩展大于原长1.5倍则取指定大小
get方法:
普通add:在末尾添加就可以了。
有指定位置的add和delete:这样的话,就需要调整一遍数组,如果在中间插入(add),就需要把其后面的往后挪一个位置。另外如果在中间删除,就需要把后面的都往前面挪一个位置。不要问为什么要挪,这是基础。
当然还有其他的一些方法例如
…
等
其他的增加,删除,修改等操作和上面讲的原理差不多,这里不赘述。
既然是可以随机访问的集合,所以ArrayList里面还自己实现了可以返回特殊位置的ListIterator,通过构造方法传入index,则可以返回一个从该位置开始的迭代器(Iterator)
“集合源码学习(二):Spliterator”
ArrayList了,当然在我刚接触Java时,甚至接触相当一段时间,我都不知道
ArrayList原理的。这两天把
ArrayList源码仔细读了一遍,感觉如果把注释完的代码贴出来还不如直接看jdk文档。我把一些核心的地方找出来,下面将从以下几个方面来学习
ArrayList。
什么是ArrayList?
其实ArrayList里面的数据结构知识就是线性表的应用。关于modCount
modCount是ArrayList继承自
AbstractArrayList的一个字段。ArrayList有个fast-fail机制。fail-fast产生的原因就在于程序在对 Collection 进行迭代(Iterator操作)时,某个线程对该 Collection 在结构上对其做了修改(add和delete操作),这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。
jdk注释为:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the vector is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future. The Enumerations returned by the elements method are not fail-fast. Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
定义
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
从定义头的代码可以看出,ArrayList继承自AbstractList,支持泛型,可以随机存取,可以克隆,可以序列化都在代码中有所体现。
是否可为null?
可以长度以及自动增长的长度
默认情况下,ArrayList内置数组长度为10,/** * 如果不指定arraylist大小,java8里面默认大小是10. */ private static final int DEFAULT_CAPACITY = 10;
如果范围不够了,自动增长长度将为原长度的1.5倍
/** * 因为是按照老数组长度大小的1.5倍去调的,所以如果给定的minCapacity没有其1.5倍这么大, * 还是会调到老数组长度1.5倍。 */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); //老数组大小的1.5倍。 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); }
当然如果主动要求扩展大于原长1.5倍则取指定大小
ArrayList的最大长度
在源码中,有MAX_ARRAY_SIZE指定
ArrayList最大长度为:
/** * array的最大长度,减去8的原因是,有些vm的header words可能会占据一些空间。 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
contains(Object o)的实现原理
实际上,在ArrayList中,需要判断
o是否为null,如果为null,则是用
=判断相等,而不为null时,则是使用equals判断。而整个判断的流程就是遍历所有元素,如果存在就返回true,否则返回false。
/** *判断是否含有某个元素。 */ public boolean contains(Object o) { return indexOf(o) >= 0; } /** * 判断的根据是遍历一遍,并且是通过equals遍历的。注意equals的用法与注意事项。 * 包括null。 */ 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; }
clone方法
ArrayList实现
clone方法其实比较简单,这里将clone方法提出来是觉得一个细节处理的很好,就是
v.modCount=0这句代码。如果没有这句代码,把本类的
modCount也克隆过去可就不好了。
/** * clone方法,注意此时还把v的modCount属性设为0了,防止由于本身是1导致结果异常。 */ 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); } }
set,get,add,remove等方法
set方法比较简单,随机访问数组,并且把该下标值设为相应值。rangeCheck为越界检查的方法。
/** * 设置泛型的类型 */ public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
get方法:
/** * 得到泛型的类型 */ public E get(int index) { rangeCheck(index); return elementData(index); }
普通add:在末尾添加就可以了。
/** * 增加泛型的类型,在末尾增加 */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
有指定位置的add和delete:这样的话,就需要调整一遍数组,如果在中间插入(add),就需要把其后面的往后挪一个位置。另外如果在中间删除,就需要把后面的都往前面挪一个位置。不要问为什么要挪,这是基础。
/** * 将特定的元素插入到特定的位置。 * 把后面的元素往后统一挪一个。 */ 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++; }
当然还有其他的一些方法例如
protected void removeRange(int fromIndex, int toIndex)
public boolean addAll(Collection<? extends E> c)
public boolean remove(Object o)
…
等
其他的增加,删除,修改等操作和上面讲的原理差不多,这里不赘述。
其他方法
另外在ArrayList还支持了对流操作:/** * 写入流 */ 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(); } } /** * 读入流里面的数据 */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } }
既然是可以随机访问的集合,所以ArrayList里面还自己实现了可以返回特殊位置的ListIterator,通过构造方法传入index,则可以返回一个从该位置开始的迭代器(Iterator)
/** * 返回一个特殊位置index的迭代器。 */ public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); }
SubList
在ArrayList中,几乎花了一半的代码来讲SubList,而SubList是什么呢?其实SubList就是ArrayList,读名字读得出来,就是子串,里面的操作基本上是和ArrayList相同,原理也相同,而使用的数组则是直接或间接使用ArrayList里面的elementData。这里就不多说了。
Java8新增的Spliterator
这里可以看我另一篇博客,里面专门以ArrayListSpliterator分析过:“集合源码学习(二):Spliterator”
相关文章推荐
- Java集合源码学习笔记(二)ArrayList分析
- Java集合源码学习笔记(五)ArrayList,LinkedList,Vector和Hashtable,HashMap的比较
- List接口实现类-ArrayList、Vector、LinkedList集合深入学习以及源码解析
- Java集合源码学习(8)_List接口的实现_CopyOnWriteArrayList
- java1.8 常用集合源码学习:ArrayList
- Java集合源码学习(6)_List接口的实现_ArrayList_Vector
- Java集合ArrayList和HashMap源码学习
- 集合类学习之Arraylist 源码分析
- 集合框架源码学习之ArrayList
- Java集合源码学习(2):ArrayList和LinkedArrayList(未完待续...)
- 集合学习--ArrayList 源码学习最终版
- Java集合源码学习(二)ArrayList分析
- Java集合之ArrayList源码概览学习
- Java集合源码剖析-ArrayList源码剖析
- 集合源码学习(八):HashSet和TreeSet
- Java 集合系列03之 ArrayList源码解析
- 【Java集合源码剖析】ArrayList源码剖析
- Java集合——ArrayList源码详解
- 第三章 JAVA集合之ArrayList源码浅析
- Java集合源码学习(18)_Map接口