Java入门记(四):Collection如何为?
摘要:本文主要介绍Collection接口的子接口List、Set、Queue之间及集成接口和实现类的关系与其下常用容器(ArrayList、LinkedList、HashMapLinkedHashMap、TreeMap、PriorityQue
目录
一、Collection及子类/接口容器继承关系
二、List
2.1 ArrayList
2.1.1 序列化的探讨
2.1.2 删除元素
2.1.3 调整大小
2.2 Vector和Stack(不建议继续使用)
2.3 抽象类AbstractSequentialList
三、Set
3.1 HashSet和LinkedHashSet
3.2 TreeSet
四、Queue
4.1 PriorityQueue
4.2 LinkedList
五、一些琐碎的话题
5.1 线程安全
5.2 clone()
5.3 foreach
5.4 null对象
Java.util中的容器又被称为Java Collections framework。虽然被称为框架,但是其主要目的是提供一组接口尽量简单而且相同、并且尽量高效、以便于开发人员按照场景选用,而不是自己重复实现的类。容器按接口可以分为两大类:Collection和Map。本文主要关注Collection,以后会将Map这块也进行研究。
一、Collection及子类/接口容器继承关系
先从Collection说起。可以看出:
1.Collection接口并不是一个根接口,它的超级接口是Iterator,需要提供其遍历、移除元素(可选操作)的能力。
2.Collection接口定义了基本的容器操作方法。
除此以外,
1.remove()和contains()判断元素是否相等的依据是类似的。
对于remove(Object o),若Collection中包含的元素e,满足(o==null ? e==null : o.equals(e)),移除其中的一个;
对于contains(Object o),若Collection中包含至少一个或多个元素e,满足(o==null ? e==null : o.equals(e)),则返回true。
2.AbstractCollection抽象类实现了一部分Collection接口的方法,主要是基于iterator实现的,如remove()、toArray(),以及利用本身的属性size实现的size()。如果读一下源码,可以发现虽然AbstractCollection利用add()实现了addAll(),但是add()本身的实现是直接抛UnsupportedOperationException异常的。实际上add()是一种“可选操作”,目的是延迟到需要时再实现。
二、List
了解了通用的Collection后,接下来,看看三大类的Collection:List、Set、Queue。首先从List说起。List中的元素是有序的,因而我们可以按序访问List中的元素,以及访问指定位置上的元素。对于“按顺序遍历访问元素”的需求,使用List的超级接口Iterator即可以做到,这也是对应抽象类AbstractList中的实现;而访问特定位置的元素(也即按索引访问)、元素的增加和删除涉及到了List中各个元素的连接关系,并没有在AbstractList中提供。
2.1 ArrayList
ArrayList是最常用的List的实现,其包装了一个用于存放元素的数组,并用size属性来标识该容器里的元素个数,而非这个被包装数组的大小。如果对数组有所了解,很容易理解ArrayList的元素是怎么编排的,各个数组的元素如何随机访问(通过索引)、元素之间如何跳转(索引增减)。阅读源码可以发现,这个数组用transient关键字修饰,表示其不会被序列化。当然,ArrayList的元素最终还是会被序列化的,要不然,这个最常用的List之一,不能持久化、不能网络传输,简直不可想象。在序列化/反序列化时,会调用ArrayList的writeObject()/readObject()方法,将该ArrayList中的元素(即0...size-1下标对应的元素)写入流/从流读出。这样做的好处是,只保存/传输有实际意义的元素,最大限度的节约了存储、传输和处理的开销。
