ArrayList
数组结构实现,查询快、增删慢,运行效率快、线程不安全。
常用方法
1.add方法添加元素
ArrayList<String> sites = new ArrayList<String>();
sites.add("a");
2.get方法访问元素
ArrayList<String> sites = new ArrayList<String>();
sites.add("a");
System.out.println(sites.get(0));//访问第一个元素
3.set方法修改元素
ArrayList<String> sites = new ArrayList<String>();
sites.add("a");
sites.set(0,"b");
4.remove方法删除元素
ArrayList<String> sites = new ArrayList<String>();
sites.add("a");
sites.remove(0);
5.size方法计算元素数量
ArrayList<String> sites = new ArrayList<String>();
sites.add("basjhd");
int num = sites.size();
System.out.println(num);
6.isEmpty方法判断是否为空
ArrayList<String> sites = new ArrayList<String>();
sites.add("a")
System.out.println(site.isEmpty())
7.foreach循环遍历
ArrayList<String> sites = new ArrayList<String>();
sites.add("a")
sites.add("b")
sites.forEach(System.out::println);
8.sort方法进行排序(字典顺序)
ArrayList<String> sites = new ArrayList<String>();
sites.add("a");
sites.add("b");
sites.add("c");
sites.add("d");
sites.add("e");
Collections.sort(sites); // 字母排序
for (String i : sites) {
System.out.println(i);
}
9.toArray方法转为数组
ArrayList<String> list1 = new ArrayList<>();
list1.add("n1");
list1.add("n2");
list1.add("n3");
String []str = list1.toArray(new String[list1.size()]);
for (String x:str){
System.out.println(x);
}
特别的几个方法
- int indexOfRange(Object o, int start, int end)
返回元素在范围内的索引
- int lastIndexOfRange(Object o, int start, int end)
返回元素在范围内的最后一个索引值
- void ensureCapacity(int minCapacity)
设置指定容器大小的arraylist
- void trimToSize()
设置容器大小为数组中的元素个数
源码解读
默认容量为10
private static final int DEFAULT_CAPACITY = 10;
实际容器
存储ArrayList元素的数组缓冲区。ArrayList的容量是该数组缓冲区的长度。添加第一个元素时,任何空ArrayList都将扩展为默认容量。
但没有添加任何元素的时候容量为0 ,添加之后容量是DEFAULTCAPACITY_EMPTY_ELEMENTDATA是10
transient Object[] elementData; // non-private to simplify nested class access
无参构造
我们可以看出无参构造的时候
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
arrayList如何添加元素(add方法)
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
解读:
首先是add方法第一行,modCount++
modCount来源于他的父级AbstractList
表示此列表在结构上被修改的次数。结构修改是指那些改变列表大小的修改,或者以某种方式扰乱列表,使得正在进行的迭代可能产生错误的结果。
迭代器和列表迭代器方法返回的迭代器和列表迭代器实现使用此字段。如果此字段的值意外更改,迭代器(或列表迭代器)将抛出ConcurrentModificationException,以响应下一个、删除、上一个、设置或添加操作。这提供了快速失效行为,而不是在迭代过程中面对并发修改时的不确定性行为。
子类使用此字段是可选的。如果子类希望提供fail fast迭代器(和列表迭代器),那么它只需在add(int,E)和remove(int)方法(以及它重写的任何其他方法,这些方法会导致列表的结构修改)中增加这个字段。对add(int,E)或remove(int)的单个调用只能向该字段添加一个,否则迭代器(和列表迭代器)将抛出虚假的ConcurrentModificationException。如果实现不希望提供故障快速迭代器,则可以忽略此字段。
由此可知modCount在这里用于记录ArrayList被添加的次数,添加一次,modCount++
第二行 add调用我想就用说了
第三行if (s == elementData.length)
对当前长度size和容器长度进行比较
我们之前说过elementData实际上是一个数组,它是ArrayList的容器
那么这一行就是在判断ArrayList的长度是否与自己的容器长度一致,以免导致出错!
第四行elementData = grow();这里很深
调用方法
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
}
我们看完发现最核心的是Arrays.copyOf(elementData, newCapacity)以及前面对容器容量的处理
其实是使用copyOf去内存的堆中复制了一个新的数组给到了elementData这个容器这样ArrayList的容器就变成了新的!
注意:我这里说的是寻常情况下(指添加元素在1~10以内)
其实是newCapability = 10,也就是直接默认创建了一个长度为10的数组
这里看不懂的话说明==》计算机底层堆和栈你没掌握,劝你不要看下去了。先去看堆和栈的解读
第五行 elementData[s] = e;
很简单让容器最后一个元素赋值
这里看不懂的话说明==》数据结构是基础你没掌握,劝你不要看下去了。先去看基础的数据结构
第六行 size = s + 1;
这就很简单了就是增加ArrayList的size长度保证ArrayList.size == elementData.length
这样就完成了ArrayList的添加!
还没完!刚刚是容器的默认容量未满的时候的add
容器满后的add核心(*)
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
}
任然是这里,我们把 minCapacity = 11,oldCapacity = 10 带入
为了大家理解我还是画图吧