一 、ArrayList基础使用
1、ArrayList结构:
顾名思义ArrayList的结构就是数组型的List。
2、添加元素:
public class Main {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("s");
System.out.println(arrayList);
}
}
运行结果:
[s]
咱们看一下add的源码:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
这样就很清楚具体的过程了,先检查数组的容量是否足够,用ensureCapacityInternal()函数检查,如果不够则会:
elementData = Arrays.copyOf(elementData, newCapacity);
将原数组复制到一个容量为newCapacity的新数组里以扩充容量,然后再将元素e添加到到数组末尾。
过程很清晰了,所以其缺点也就很明显了,当数组容量不够时需要复制生成新的数组这个开销挺大的。
3、添加元素到任意位置:
public class Main {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("a");
arrayList.add("b");
arrayList.add("c");
arrayList.add(0,"d");//将d插入到位置0
System.out.println(arrayList);
}
}
运行结果:
[d, a, b, c]
其中的细节为从将要插入的下标开始到数组最后一个元素整体向后移一格即为复制过程,所以该方式的缺点也十分明显,如果需要将一个元素插入到一个较大数组的前排位置,则需要复制大量元素,开销会很大。
4、删除某个元素
public class Main {
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("a");
arrayList.add("b");
arrayList.add("c");
arrayList.add(0,"d");
System.out.println(arrayList);
arrayList.remove(0);//删除下标为0的元素
System.out.println(arrayList);
}
}
运行结果:
[d, a, b, c]
[a, b, c]
删除过程和上一个插入任何位置过程类似,当删除某个下标的元素后,需要该下标后面的元素都前移一格即复制过程,所以当一个很大的数组需要删除前排某个元素的时候会产生很大的开销。
二、LinkedList基础使用
1、LinkedList结构
顾名思义,Linked为连接的意思所以LinkedList是以链表方式实现的List。
2、添加元素:
public class Main {
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
System.out.println(linkedList);
}
}
运行结果:
[a, b, c]
咱们看一下add的源代码:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
从这段代码中可以很清楚地看出LinkedList是一条双链表,往里面添加元素需要新建一个Node对象然后连接好前后对象,这样的好处是不需要像ArrayList那样考虑容量问题,但是如果添加很多元素进来这样的方式开销也不小。
3、添加元素到任意位置:
public class Main {
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
System.out.println(linkedList);
linkedList.add(0,"d");//把d插入到下标为0处
System.out.println(linkedList);
}
}
运行结果:
[a, b, c]
[d, a, b, c]
根据链表的特点,需要先找到要插入位置的后一个Node,因为链表没有真正的下标:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //根据index大小分成从头或从尾开始找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x; //找到后返回
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x; //找到后返回
}
}
所以主要开销是花费在找Node上面,当元素很多的情况下,要插入的元素又在中间位置时查找的开销会比较大。
4、删除元素:
public class Main {
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
System.out.println(linkedList);
linkedList.add(0,"d");
System.out.println(linkedList);
linkedList.remove(0);//删除下标为0位置的元素
System.out.println(linkedList);
}
}
运行结果:
[a, b, c]
[d, a, b, c]
[a, b, c]
同样的道理根据链表的特点,删除一个元素需要先找到该元素Node,因为链表没有真正的下标:
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { //根据index大小分成从头或从尾开始找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x; //找到后返回
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x; //找到后返回
}
}
找到后就修改下前后Node的指针就可以了。如果元素很多而且要删除的元素在中间位置的话可能找起来会花费很多开销。