目录
- List集合系列
- List系列集合特点
- List集合特有方法
- List集合的遍历方式
- ArrayList集合的底层原理
- 分析源码
- LinkedList集合的底层原理
- 集合的并发修改异常问题(删除重复元素时)
List集合系列
List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引。
- 有序:存储和取出的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
List集合特有方法
List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
List集合的遍历方式
List集合的遍历方式有几种?
- 迭代器 (Collection集合)
- 增强for循环(Collection集合)
- Lambda表达式(Collection集合)
- for循环(因为List集合存在索引,独有)
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("String1");
list.add("String2");
list.add("String3");
list.add("String4");
//for 循环遍历
for (int i = 0; i < list.size(); i++) {
String element=list.get(i);
System.out.println(element);
}
}
ArrayList集合的底层原理
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
分析源码
第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
什么时候扩容:
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)//s为当前集合的长度,当它等于现在集合的最大长度时,调用grow()方法扩容
elementData = grow();
elementData[s] = e;//否则就插入集合
size = s + 1;//集合当前长度加一
}
如何扩容:
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
private Object[] grow(int minCapacity) {//minCapcity=size+1
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)];
}
}
private Object[] grow() {
return grow(size + 1);
}
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);
}
}
当ArrayList的容量扩容1.5倍后不会超过规定的最大值,则扩大为原来的1.5倍,即newCapacity=oldCapacity+(oldCapacity >> 1)
当ArrayList的容量扩容1.5后超过规定的最大值,则新的容量为原来的容量加上此次要增加的容量,调append()时,加的恰好为1,即newCapacity=oldCapacity+(minCapacity - oldCapacity)
综上,AraayList的扩容刚开始每次是原来的1.5倍,超过一个临界点会根据需求扩容。
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
@IntrinsicCandidate
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
并且,可以看到数据最后是调用了System.arraycopy()方法,来将原来的数据复制到新的数组中。
LinkedList集合的底层原理
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
集合的并发修改异常问题(删除重复元素时)
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。
那么哪些遍历存在问题?
- 迭代器遍历集合且直接用集合删除重复元素的时候可能出现。
- 增强for循环遍历集合且直接用集合删除重复元素的时候可能出现。
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("String1");
list.add("String1");
list.add("String2");
list.add("String3");
Iterator<String> i= list.iterator();
while (i.hasNext()){
String element=i.next();
if ("String1".equals(element)){
//list.remove(element);//会报异常,原因集合会自动前移,会漏删
i.remove();//正确:使用迭代器删除,内部实现了--
}
}
System.out.println(list);
}
迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
增强for无法解决
for循环可以从后往前删或则每次删除减一