源码学习之ArrayList
2016-05-26 10:07
363 查看
ArrayList源码分析学习
首先是ArrayList的定义//继承AbstractList抽线类,实现了List、RandomAccess、Cloneable和Serializable接口 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList源码开头定义的几个成员变量
//表示ArrayList的默认容量为10,但是在我们初始化为一个空的ArrayList的时候容量并不是10,具体下文解释 private static final int DEFAULT_CAPACITY = 10; //表示空的一个元素数组,在初始化一个空的ArrayList的时候,elementData就等于这个EMPTY_ELEMENTDATA private static final Object[] EMPTY_ELEMENTDATA = {}; //这个就是实际存储数据的数组,从这个可以看出ArrayList是数组实现的 transient Object[] elementData; //这个就是数组列表的存储数据的size了 private int size;
构造函数,ArrayList一共提供了三个构造函数。
//第一个构造函数,参数initialCapacity为初始化容量,如果initialCapacity为负,则抛出IllegalArgumentException异常,否则就初始化elementData为一个大小为initialCapacity的数组 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity); this.elementData = new Object[initialCapacity]; } //第二个构造函数,不传入参数,这个时候elementData,就设置成上文定义的空数组,EMPTY_ELEMENTDATA,这个时候数组链表的容量是0,并不是10. public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; } //第三个构造函数,传入参数为另一个集合。这个时候就是用传入集合的元素初始化数组链表。 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }
ArryList扩容机制
//保证容量,这块是外部手动调用扩容的。minCapacity是需要的最小容量。 public void ensureCapacity(int minCapacity) { //最小扩展,如果elementData等于EMPTY_ELEMENTDATA,那么minExpand设置为DEFAULT_CAPACITY,否则minExpand等于0 int minExpand = (elementData != EMPTY_ELEMENTDATA)? 0: DEFAULT_CAPACITY; //如果最小需要容量大于最小扩展,那么这个时候需要保证最小需要容量的数值 if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } }
//保证容量,这是内部函数调用用来扩容的,在每次添加元素的时候都会调用 private void ensureCapacityInternal(int minCapacity) { //elementData == EMPTY_ELEMENTDATA时,这个时候minCapacity去DEFAULT_CAPACITY和minCapacity大的一个。 //在初始化一个空的ArrayList时,elementData == EMPTY_ELEMENTDATA,这个时候添加一个元素,检查容量,minCapacity传递的是size+1,也就是1,这个时候minCapacity等于DEFAULT_CAPACITY。 if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
//上面两个函数调用的函数 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code //如果需要保证的最小容量大于elementData的长度,那么这个时候需要扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); }
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //具体的扩容函数 private void grow(int minCapacity) { int oldCapacity = elementData.length; //新容量,是原来的容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //如果新容量不能满足最小容量要求,那么新容量设置成minCapacity //从空添加一个元素的时候,minCapacity==DEFAULT_CAPACITY,这个时候容量就扩充到DEFAULT_CAPACITY了。 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //如果新容量大于MAX_ARRAY_SIZE,那么新容量设置成Integer.MAX_VALUE 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_ARRAY_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; }
总结来说,扩容的机制是在每次添加元素的时候检查容量size+add_size,elementData是空数组的时候,容量会变成DEFAULT_CAPACITY,也就是10。elementData不是空数组的时候,如果size+add_size大于容量,也即是elementData.length,那么就扩容,扩容的方法是首先扩容1.5倍的原容量,然后判断1.5的原容量是否大于size+add_size,如果不大于,那么新容量设置成size+add_size,再检查size+add_size是否大于最大容量Integer.MAX_VALUE - 8,如果大于那么就设置成Integer.MAX_VALUE
ArrayList常用操作
size()
public int size() { return size; }
isEmpty()
public boolean isEmpty() { return size == 0; }
contains()
public boolean contains(Object o) { return indexOf(o) >= 0; } //找到elementData中第一次出现o的下标 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; }
get()
public E get(int index) { //是否越界判断 rangeCheck(index); return elementData(index); } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
set()
//修改下标index处的元素 public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
add()
//在后面添加一个元素 public boolean add(E e) { //判断容量 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } //在下标index处添加一个元素,有数据的移动 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++; }
remove()
//删除下标为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; } //删除赌徒个等于o的元素 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; }
clear
public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- C#.Net ArrayList的使用方法
- 浅析Ruby的源代码布局及其编程风格
- VBS ArrayList Class vbs中的数组类
- asp.net 抓取网页源码三种实现方法
- C#中Arraylist的sort函数用法实例分析
- C#中ArrayList的使用方法
- C#中Array与ArrayList用法及转换的方法
- JS小游戏之仙剑翻牌源码详解
- JS小游戏之宇宙战机源码详解
- jQuery源码分析之jQuery中的循环技巧详解
- 本人自用的global.js库源码分享
- java中原码、反码与补码的问题分析
- ASP.NET使用HttpWebRequest读取远程网页源代码
- C#生成随机ArrayList的方法
- c# ArrayList的使用方法小总结
- PHP网页游戏学习之Xnova(ogame)源码解读(六)
- C#获取网页HTML源码实例
- PHP实现C#山寨ArrayList的方法
- PHP网页游戏学习之Xnova(ogame)源码解读(八)