ArrayList定义只定义类两个私有属性:
private transient Object[] elementData;//elementData存储ArrayList内的元素
private int size;//size表示它包含的元素的数量Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
ArrayList的构造函数:
第一个构造方法使用提供的initialCapacity来初始化elementData数组的大小。
第二个构造方法调用第一个构造方法并传入参数10,即默认elementData数组的大小为10。
第三个构造方法则将提供的集合转成数组返回给elementData(返回若不是Object[]将调用Arrays.copyOf方法将其转为Object[])。
add(E e)
add(E e)都知道是在尾部添加一个元素,如何实现的呢?
public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
看到add(E e)中先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增。例如初次添加时,size为0,add将elementData[0]赋值为e,然后size设置为1(类似执行以下两条语句elementData[0]=e;size=1)。将元素的索引赋给elementData[size]不是会出现数组越界的情况吗?这里关键就在ensureCapacity(size+1)中了。
根据ensureCapacity的方法名可以知道是确保容量用的。ensureCapacity(size+1)后面的注释可以明白是增加modCount的值(加了俩感叹号,应该蛮重要的,来看看)。
public void ensureCapacity(int minCapacity) { 9 modCount++; 10 int oldCapacity = elementData.length; 11 if (minCapacity > oldCapacity) { 12 Object oldData[] = elementData; 13 int newCapacity = (oldCapacity * 3)/2 + 1; 14 if (newCapacity < minCapacity) 15 newCapacity = minCapacity; 16 // minCapacity is usually close to size, so this is a win: 17 elementData = Arrays.copyOf(elementData, newCapacity); 18 } 19 }
The number of times this list has been structurally modified.
这是对modCount的解释,意为记录list结构被改变的次数(观察源码可以发现每次调用ensureCapacoty方法,modCount的值都将增加,但未必数组结构会改变,所以感觉对modCount的解释不是很到位)。
增加modCount之后,判断minCapacity(即size+1)是否大于oldCapacity(即elementData.length),若大于,则调整容量为max((oldCapacity*3)/2+1,minCapacity),调整elementData容量为新的容量,即将返回一个内容为原数组元素,大小为新容量的数组赋给elementData;否则不做操作。
所以调用ensureCapacity至少将elementData的容量增加的1,所以elementData[size]不会出现越界的情况。
add(int index, E element)
public void add(int index, E element) { 2 if (index > size || index < 0) 3 throw new IndexOutOfBoundsException( 4 "Index: "+index+", Size: "+size); 5 6 ensureCapacity(size+1); // Increments modCount!! 7 System.arraycopy(elementData, index, elementData, index + 1, 8 size - index); 9 elementData[index] = element; 10 size++; 11 }
首先判断指定位置index是否超出elementData的界限,之后调用ensureCapacity调整容量(若容量足够则不会拓展),调用System.arraycopy将elementData从index开始的size-index个元素复制到index+1至size+1的位置(即index开始的元素都向后移动一个位置),然后将index位置的值指向element。
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。
对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。
底层实际将将该元素作为key放入HashMap。
120 * 由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key
121 * 与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true),
122 * 新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变,
123 * 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中,
124 * 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。
125 * @param e 将添加到此set中的元素。
126 * @return 如果此set尚未包含指定元素,则返回true。
public boolean add(E e) {
129 return map.put(e, PRESENT)==null;
130 }