4 minutes
Java基础之集合-ArrayList
Java基础之集合总览 - ArrayList
Java集合是经常使用到的,集合类主要由Collection 和 Map 这两个接口派生。
Collection 派生出 Set,List, Queue。 Map派生HashMap, TreeMap, ConcurrentHashMap
Collection常用方法
// 返回集合中元素个数
int size();
//Returns true if this collection contains no elements.
boolean isEmpty();
// 判断集合中是否存在元素o , (o==null ? e==null : o.equals(e)).
boolean contains(Object o);
// 添加元素
boolean add(E e);
// 删除元素
boolean remove(Object o);
// 清空元素
void clear();
Map常用方法
// 返回 k-v 数量
int size();
// Returns <tt>true</tt> if this map contains no key-value mappings.
boolean isEmpty();
// 存在key就返回true,
boolean containsKey(Object key);
boolean containsValue(Object value);
// 放入k,v , key可以为null
V put(K key, V value);
// 删除k-v, key可以为null
V remove(Object key);
// 返回key集合
Set<K> keySet();
// 返回key-value 集合
Set<Map.Entry<K, V>> entrySet();
// 返回value集合
Collection<V> values();
// equals 比较
// (e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey()))
// &&
// (e1.getValue()==null ? e2.getValue()==null : e1.getValue().equals(e2.getValue()))
boolean equals(Object o);
// hashcode
// (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ (e.getValue()==null ? 0 : e.getValue().hashCode())
int hashCode();
常用的集合 - ArrayList
日常开发最常用最基础的集合:
- ArrayList、LinkedList
- HashMap、ConcurrentHashMap
首先看ArrayList
ArrayList
首先看声明
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{}
ArrayList继承了AbstractList ,实现了 List等接口。
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 共享的空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认容量的空数组实例。我们用这个区分 EMPTY_ELEMENTDATA, 是为了弄清楚首个元素添加时膨胀多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 数组缓存存入的元素。这个数组的长度就是它的容量。任何等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空数组添加首个元素时会被扩展为默认长度
transient Object[] elementData; // non-private to simplify nested class access
// 包含几个元素
private int size;
以上几个初始定义,就是ArrayList 经常用的东西, 长度保存在size 里, 元素保存在 Object[] elementData。 我们这里知道ArrayList 是由数组实现的。
继续看构造方法。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
当你用 new ArrayList() 时候, ArrayList 内部发生的就是把 elementData 初始化为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 也就是把空对象数组实例给它,此时它的容量也初始为10。
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);
}
}
使用如 new ArrayList(50) 时候, 实际调用的这个构造方法。传入的int型初始化容量要>=0, 否则就是报不合法容量异常。
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 {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
使用如 new ArrayList(otherList); 时候, 先把传入的集合转成了数组,size 被复制为传入集合的长度,如果是空集合,就用EMPTY_ELEMENTDATA来初始化,这是就相当于 new ArrayList(); 如果非空集合,那么比较传入对象信息,如何是ArrayList 就直接把它元素复制给 elementData. 否则就是复制元素给elementData.
add
我们来看add方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
添加元素时候,首先是 ensureCapacityInternal(size+1) ,这个方法先不看,操作elementData,size+1 的位置放添加的元素e . 要保证成功放入元素,就要容量合适,所以ensureCapacityInternal 肯定会有容量操作的内容,带着思考去看ensureCapacityInternal.
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
首先ensureCapacityInternal 的参数 minCapacity 就是新增元素后的最新长度,该方法只有一行实现: ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
里面的方法是 calculateCapacity(elementData, minCapacity)); 名字就是计算容量,它的具体实现就是首先判断出元素数组 elementData是否就是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 如果是就返回 max(DEFAULT_CAPACITY, minCapacity); DEFAULTCAPACITY_EMPTY_ELEMENTDATA 就是在 new ArrayList() 时候初始话的空数组实例。 DEFAULT_CAPACITY 就是 10 , 所以if里面返回的就是 默认容量10 ,或者真实容量。 如果不是初始化0元素的数组,就直接返回真实长度。 总的来说,就是当第一次添加元素时候,走if里面的逻辑,返回默认容量10, 否则就返回真实容量。
再来看 ensureExplicitCapacity 方法, 首先 modCount++; 然后 如果 minCapacity - elementData.length >0,也就是 minCapacity > elementData.length , 就调用 grow方法。也就是初始化时候是10, 超过10就增长 10 + 10/2 = 15 。
我们具体看grow方法:
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);
}
注意newCapacity = oldCapacity + (oldCapacity » 1); 这里是新容量增长多少, 其实就是增加原来容量的一半。
如果 newCapacity < minCapacity 那么newCapacity就是 minCapacity
如果 newCapacity > MAX_ARRAY_SIZE , 那么新容量就是 hugeCapacity(minCapacity), 这里MAX_ARRAY_SIZE 是
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
也就是 2^31-1 - 8 . 最后调用 Arrays.copyOf 复制到新容量的数组。
其中如果 newCapacity > MAX_ARRAY_SIZE (2^31-1-8) , 那么最大容量会扩容:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
当 minCapacity超过Integer.MAX_VALUE 时候会变成负数,这时就是内存溢出错误。
remove
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;
}
remove方法, 首先是rankCheck, 检查要删的下表是否越界。需要移动的元素数量是 size - index -1; 然后复制到新数组,并把删除的位置index元素置为 null, 返回删除的元素。
我们主要看下移动了多少,System.arraycopy 。 上个 System.arraycopy(elementData, index+1, elementData, index, numMoved);
就是 本数组 index+1开始复制, 复制到本数组的 index 开始, numMoved 个元素。 最后一位是空的,就把它置为null.
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
clear
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
clear方法, for循环把所有的元素置为null, 然后size置为0