List接口的实现类

List表示有序的集合(元素可以重复),根据索引来寻找元素,放入其中的元素的存储顺序和放入顺序是一致的。

ArrayList

al-1

0.继承自AbstractList,拥有通用的方法如Iterator迭代器。实现List接口。

1.底层是transient Object[] elementData 数组。可以看到默认大小是10。

al-2

2.不同的初始化方式,有一点区别。

al-3

1
2
3
4
5
6
7
8
// 未指定,默认是10。构造的数组大小为0,要等到放入第一个元素时才会扩容成10个。
List<String> list1 = new ArrayList<>();

// 构造的数组大小为0。
List<String> list2 = new ArrayList<>(0);

// 构造的数组大小为14
List<String> list3 = new ArrayList<>(14);

3.数组扩容

al-4

默认初始化的内部数组大小是10,当放入第11个元素时会进行第一次扩容:newCapacity = oldCapacity + oldCapacity >> 1 ,也就是变成原来的1.5倍。(所以默认情况下,放入第11个元素时,扩容成15个;放入第16个元素时,扩容成22个;放入第23个元素时,扩容成33个。)

数组扩容的操作是进行数组的复制,所以扩容消耗资源,应该尽量先指明所需的容量,来减少扩容操作。对于已经存在的ArrayList,在放入大量元素前,可以手动进行扩容:ArrayList.ensureCapacity(capacity) 来指定内部数组的大小。

4.有一个骚操作,把ArrayList转化成对象数组:

1
2
3
4
// 调用toArray方法,传入对象数组接收,返回Object[]数组
Object[] objects2 = appleList.toArray(new Apple[0]);
// 类型转换
Apple[] apples3 = (Apple[]) objects2;

5.ArrayList的add(int index, Object obj)remove(int index) / remove(Object obj)都会进行数组的复制,所以ArrayList适合随机访问get(int index),不适合随机增加、删除操作多的场景。

al-5

ArrayList总结

1.初始化时应该指明容量,避免多次的扩容操作。

2.使用场景应该是随机查找比较多而随机增加、删除操作比较少的场景。

al-6

LinkedList

al-7

1.继承自AbstractSequentialList,拥有通用的方法如iterator。实现List接口。实现Queue接口,拥有队列的特性;实现Deque接口,拥有双端队列的特性。

2.LinkedList内部的节点。拥有前后指针,实现的是双端队列的性质。

ll-2

内部的私有属性,存储了链表的节点个数以及保存了链表的头尾指针。

ll-3

3.因为LinkedList实现了Queue接口、Deque接口,所以它既能作为队列也能作为堆栈来使用。又因为实现了List接口,所以又是有序的Collection。这意味着它有3种数据结构的作用,我们应该在正确的场景下正确语义化使用,这表示我们要使用合适的接口来声明LinedList。(因为双端队列Deque接口提供了传统Stack的操作方法声明,所以它实际上是4种数据结构)

ll-4

ll-5

ll-6

ll-7

4.对于LinkedList的随机访问操作,它内部有一个优化。如果index<(size/2),就从头部开始查找;否则从尾部开始查找。

ll-8

ll-9

LinkedList总结

1.使用场景应该是增加、删除操作多的情况,而随机访问操作少的情况。

2.LinkedList有3种数据结构的身份,我们应该在正确的场景下进行正确的接口声明,并且使用与之对应的语义化方法来操作LinkedList。

Vector

vector作为古老的集合类,是jdk1.2之后才改为实现List接口的。它与ArrayList的性质很像(2者的继承图是一样的),但是其内部方法都使用了synchronized修饰来保证线程安全,这使得对Vector的操作在多线程下变成了串行操作。Vector已经不推荐使用了,单线程下我们选用ArrayList,多线程下我们选用CopyOnWriteArray。

vl-1

1.默认初始化大小为10,扩容操作时,如果构造时指明了增大的容量,则增加;否则默认变成原来的2倍。

vl-2

vl-3

Stack

Stack作为Vector的子类,可用于实现堆栈。但是不建议使用,而是使用LinkedList来替代堆栈。

更早的文章

动态代理

1、先谈静态代理对于静态代理,我们已经很熟悉了。我们拥有一个抽象类,真实类继承自抽象类并重写其业务方法,代理类持有真实类的对象实例,在重写业务方法中通过调用真实类的方法,并且添加自己的逻辑。这样代理类就实现了对真实类的行为代理。 静态代理的缺点在于,我们需要实现多个代理类,这无疑是很崩溃的。 2、 …

继续阅读