一、ArrayList简述:
ArrayList是线程非安全的数组列表,优势在于数组结构,查询效率高,修改效率较低;由下图的类图,可以看出
ArrayList是可以序列化,复制的;
二、构造函数
1、自定义初始化容量大小,int类型,值为非负数
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);
}
}
2、无参构造,此时数组为空,直到第一次添加元素时,才会定义为默认初始化大小10
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
3、带Collection对象,将Collection对象转换为数组,并将引用赋予elementData,若对象有值,利用System.copy赋值给elementData
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
三、常用方法
- 最常用的添加元素,add的两个方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- 判断 elementData 是否需要扩容,并且记录 modCount 增一,表示一次列表大小的修改
- 检查 elementData 是否已经初始化过大小,如果没有,则进行第一次分配空间,分配默认空间大小与需要的空间大小中更大的值所需空间(可能第一次调用的就是addAll(Collection<? extends E> c)方法,导致第一次分配空间就不是默认大小)
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
-
modCount++;记录一次大小的变更,然后判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
-
扩容操作;首先获得原先的数组的elementData长度,然后获得大约原空间的1.5倍的大小(此处使用位操作,提高效率);然后是判断1.5倍空间是否满足需求,满足,则赋值为需要的空间(但是minCapacity可能会超过int取值范围上限,所以需要下一步);接着如果minCapacity超过上限抛出OOM,否则判断minCapacity是否超过数组的缺省上限,超过:int上限,不超过:数组缺省上限(Integer.MAX_VALUE - 8,这是由于jvm中数组头部会存储长度信息,但是这里在minCapacity超过缺省上限以后,允许给到int上限,我也不知道原因,如果有知道的欢迎交流);最后执行Arrays.copyOf,将原数组数据copy到新数组中
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); }
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
- 检查 elementData 是否已经初始化过大小,如果没有,则进行第一次分配空间,分配默认空间大小与需要的空间大小中更大的值所需空间(可能第一次调用的就是addAll(Collection<? extends E> c)方法,导致第一次分配空间就不是默认大小)
- 将指定添加元素,添加至elementData数组的末尾
2、contains方法,仅仅是遍历列表数据,直到找寻到数据为止;如果数据量较大,建议使用HashSet(基于HashMap的方式)
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
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;
}
3、clear方法,遍历列表,将数组引用指向null,但是数组本身长度不变,仍然占用内存,引用对象所占内存需要等gc判断是否有其它引用后选择回收与否。
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
// 将数组引用指向null
elementData[i] = null;
size = 0;
}
其实还有些方法值得看一下,等后面有时间再去补充吧......