Java集合框架总结

  Java中我们用集合来存储和操作数目不固定的一组数据,集合中存放的都是对象的引用,而非对象本身,这也是集合中不能存储基本类型数据的原因。集合类大都在java.util包中,java集合架构主要有两个接口Collection和Map供子类实现,java集合架构支持3种类型的集合:规则集(Set),线性表(List),和图(Map),分别定义在Set,List,Map中。Set实例存储一组互不相同的元素(集合),List实例存储一组顺序排列的元素(表),Map存储一组 对象---关键值的映射。

  学习java集合框架的内容,个人认为要做到熟悉以下内容:

一、熟悉常用java集合框架的层次结构

Java集合框架总结

Java集合框架总结

二、常用集合类的特性及底层的结构

  ArrayList底层是一个数组,数组中元素类型相同,每个元素占用的空间大小是相同的,并且整个数组的储存空间是连续的,因此只要知道首元素地址,再加上下标,通过数学表达式可以迅速定位到数组中任意一个元素,查找效率高,缺点是为了保证连续,每次增删操作涉及到其后元素的位移,在实际使用中利用下标查询的机会较少,为了提高数组查询效率,会使用折半查找(二分法),前提是数组内元素按大小排好序。

  LinkedList底层是双向链表,除了首元素和尾元素外,其余每个元素都有索引指向其前一个元素和后一个元素,因此增删效率比较高,查询时必须从首元素开始一个一个节点向下找,所以查询效率低。

       LinkedList间接实现了Queue(队列)接口,因此可以利用其模拟队列结构,代码如下:

Java集合框架总结

同时也可以利用LinkedList模拟出栈数据结构,代码如下:

Java集合框架总结

  HashMap以<K,V>的形式存储键值对,底层是哈希表,哈希表既不是一个纯属组,也不是纯链表,而是一个链表和数组的结合体,可以把哈希表看作是一个每个节点都是一个数组的单向链表,存储在HashMap中的需要同时重写hashcode()和equals()方法。HashMap中的key部分便是一个HashSet,HashMap具有查询和增删效率都很高的特点

  LikedHashMap:按照添加顺序存储,可以按添加顺序取出,通过我们利用其在前台生成顺序固定的下拉列表,其key部分就是LikedHashSet。

  TreeHashMap:存放在其中的元素可以按照一定的规则进行排序,因此存放其中的元素必须实现Comparable接口,或者单独编一个比较器实现Comparator接口,通常如果需要放入TreeHashMap元素的类型较多且比较规则相同时,推荐后者,实现代码复用。TreeHashMap底层是可排序二叉树,它遵循左小又大的原则,其底层对其进行遍历时采用中序遍历,因此迭代出来的元素总动从下到大排列,其key值部分就是一个TreeSet。

三、使用Collection接口和Map接口定义的公用方法对集合进行操作

1.Collection 接口

用于表示任何对象或元素组。想要尽可能以常规方式处理一组元素时,就使用这一接口。

(1) 单元素添加、删除操作:

boolean add(Object o):将对象添加给集合

boolean remove(Object o): 如果集合中有与o相匹配的对象,则删除对象o  

(2) 查询操作:  

int size() :返回当前集合中元素的数量

 boolean isEmpty() :判断集合中是否有任何元素

boolean contains(Object o) :查找集合中是否含有对象o

Iterator iterator() :返回一个迭代器,用来访问集合中的各个元素

(3) 组操作 :作用于元素组或整个集合

boolean containsAll(Collection c): 查找集合中是否含有集合c 中所有元素

boolean addAll(Collection c) : 将集合c 中所有元素添加给该集合

void clear(): 删除集合中所有元素

void removeAll(Collection c) : 从集合中删除集合c 中的所有元素

void retainAll(Collection c) : 从集合中删除集合c 中不包含的元素

(4) Collection转换为Object数组 :

Object[] toArray() :返回一个内含集合所有元素的array

Object[] toArray(Object[] a) :返回一个内含集合所有元素的array。运行期返回的array和参数a的型别相同,需要转换为正确型别。

此外,您还可以把集合转换成其它任何其它的对象数组。但是,您不能直接把集合转换成基本数据类型的数组,因为集合必须持有对象。

2.Iterator 接口 
 Collection 接口的iterator()方法返回一个 Iterator。Iterator接口方法能以迭代方式逐个访问集合中各个元素,并安全的从Collection 中除去适当的元素。 
(1) boolean hasNext(): 判断是否存在另一个可访问的元素 。
(2) void remove(): 删除上次访问返回的对象。本方法必须紧跟在一个元素的访问后执行。Iterraor.remove是在遍历集合中过程中删除元素的唯一安全的方法。

3.List接口

List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。

面向位置的操作包括插入某个元素或 Collection 的功能,还包括获取、除去或更改元素的功能。在 List 中搜索元素可以从列表的头部或尾部开始,如果找到元素,还将报告元素所在的位置 :

void add(int index, Object element): 在指定位置index上添加元素element

boolean addAll(int index, Collection c): 将集合c的所有元素添加到指定位置index

Object get(int index): 返回List中指定位置的元素

int indexOf(Object o): 返回第一个出现元素o的位置,否则返回-1

int lastIndexOf(Object o) :返回最后一个出现元素o的位置,否则返回-1

Object remove(int index) :删除指定位置上的元素

Object set(int index, Object element) :用元素element取代位置index上的元素,并且返回旧的元素

4.ListIterator接口

ListIterator 接口继承 Iterator 接口以支持添加或更改底层集合中的元素,还支持双向访问。ListIterator没有当前位置,光标位于调用previous和next方法返回的值之间。一个长度为n的列表,有n+1个有效索引值:

(1) void add(Object o): 将对象o添加到当前位置的前面

void set(Object o): 用对象o替代next或previous方法访问的上一个元素。如果上次调用后列表结构被修改了,那么将抛出IllegalStateException异常。  

(2) boolean hasPrevious(): 判断向后迭代时是否有元素可访问 Object previous():返回上一个对象

 int nextIndex(): 返回下次调用next方法时将返回的元素的索引

 int previousIndex(): 返回下次调用previous方法时将返回的元素的索引

 正常情况下,不用ListIterator改变某次遍历集合元素的方向 — 向前或者向后。虽然在技术上可以实现,但previous() 后立刻调用next(),返回的是同一个元素。把调用 next()和previous()的顺序颠倒一下,结果相同。

5. Map接口

Map接口不是Collection接口的继承。Map接口用于维护键/值对(key/value pairs)。该接口描述了从不重复的键到值的映射。

(1) 添加、删除操作:

Object put(Object key, Object value): 将互相关联的一个关键字与一个值放入该映像。如果该关键字已经存在,那么与此关键字相关的新值将取代旧值。方法返回关键字的旧值,如果关键字原先并不存在,则返回null

Object remove(Object key): 从映像中删除与key相关的映射

void putAll(Map t): 将来自特定映像的所有元素添加给该映像

void clear(): 从映像中删除所有映射 “键和值都可以为null。但是,您不能把Map作为一个键或值添加给自身。”

(2) 查询操作:  

Object get(Object key): 获得与关键字key相关的值,并且返回与关键字key相关的对象,如果没有在该映像中找到该关键字,则返回null  

boolean containsKey(Object key): 判断映像中是否存在关键字key  

boolean containsValue(Object value): 判断映像中是否存在值value

int size(): 返回当前映像中映射的数量

boolean isEmpty() :判断映像中是否有任何映射

(3) 视图操作 :处理映像中键/值对组

Set keySet(): 返回映像中所有关键字的视图集

因为映射中键的集合必须是唯一的,您用Set支持。你还可以从视图中删除元素,同时,关键字和它相关的值将从源映像中被删除,但是你不能添加任何元素。

Collection values():返回映像中所有值的视图集

因为映射中值的集合不是唯一的,您用Collection支持。你还可以从视图中删除元素,同时,值和它的关键字将从源映像中被删除,但是你不能添加任何元素。

Set entrySet(): 返回Map.Entry对象的视图集,即映像中的关键字/值对

 因为映射是唯一的,您用Set支持。你还可以从视图中删除元素,同时,这些元素将从源映像中被删除,但是你不能添加任何元素。

6Map.Entry接口

Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。集合中每个对象都是底层Map中一个特定的键/值对。 通过这个集合的迭代器,您可以获得每一个条目(唯一获取方式)的键或值并对值进行更改。当条目通过迭代器返回后,除非是迭代器自身的remove()方 法或者迭代器返回的条目的setValue()方法,其余对源Map外部的修改都会导致此条目集变得无效,同时产生条目行为未定义。  

(1) Object getKey(): 返回条目的关键字  

(2) Object getValue(): 返回条目的值  

(3) Object setValue(Object value): 将相关映像中的值改为value,并且返回旧值

四、Iterator接口遍历集合和使用JDK的增强for循环进行集合遍历

1.ArrayList

Java集合框架总结

2.HashMap

 Java集合框架总结

五、会使用Collections类的静态方法

Collections工具类提供了大量针对Collection/Map的操作,总体可分为四类:

1. 排序操作(主要针对List接口相关)

    reverse(List list):反转指定List集合中元素的顺序

    shuffle(List list):对List中的元素进行随机排序(洗牌)

    sort(List list):对List里的元素根据自然升序排序

    sort(List list, Comparator c):自定义比较器进行排序

    swap(List list, int i, int j):将指定List集合中i处元素和j出元素进行交换

    rotate(List list, int distance):将所有元素向右移位指定长度,如果distance等于size那么结果不变

2. 查找和替换(主要针对Collection接口相关)

    binarySearch(List list, Object key):使用二分搜索法,以获得指定对象在List中的索引,前提是集合已经排序

    max(Collection coll):返回最大元素

    max(Collection coll, Comparator comp):根据自定义比较器,返回最大元素

    min(Collection coll):返回最小元素

    min(Collection coll, Comparator comp):根据自定义比较器,返回最小元素

    fill(List list, Object obj):使用指定对象填充

    frequency(Collection Object o):返回指定集合中指定对象出现的次数

    replaceAll(List list, Object old, Object new):替换

 3. 同步控制

Collections工具类中提供了多个synchronizedXxx方法,该方法返回指定集合对象对应的同步对象,从而解决多线程并发访问集合时线程的安全问题。HashSet、ArrayList、HashMap都是线程不安全的,如果需要考虑同步,则使用这些方法。这些方法主要有:synchronizedSet、synchronizedSortedSet、synchronizedList、synchronizedMap、synchronizedSortedMap。

特别需要指出的是,在使用迭代方法遍历集合时需要手工同步返回的集合。

4. 设置不可变集

Collections有三类方法可返回一个不可变集合

    emptyXxx():返回一个空的不可变的集合对象

    singletonXxx():返回一个只包含指定对象的,不可变的集合对象。

    unmodifiableXxx():返回指定集合对象的不可变视图

六、关于集合类的一些其它补充

1.多线程环境下集合的使用:

我们知道, HashMap 是非线程安全的,当我们只有一个线程在使用HashMap 的时候,自然不会有问题,但如果涉及到多个线程,并且有读有写的过程中, HashMap就不能满足我们的需要了(fail-fast)。在不考虑性能问题的时候,我们的解决方案有Hashtable 或者Collections提供的同步方法,这两种方式基本都是对整个 hash 表结构做锁定操作的,这样在锁表的期间,别的线程就需要等待了,无疑性能不高。

此时我们可以考虑使用ConcurrentHashMap,用来替代同步Map,它们在本质上使用了更粗粒度的锁,在迭代过程中仅仅锁定容器的一部分,因而大大提高了多线程环境下程序的执行效率。

CopyOnWriteArrayList:list的实现每一次更新都会产生一个新的隐含数组副本,所以这个操作成本很高。通常用在遍历操作比更新操作多的集合,比如listeners/observers集合。

2.如何正确的边遍历边移除Collection中的元素

边遍历边移除Collection中的元素的唯一正确方式是用iterator.remove()方法

3.如何删除ArrayList中重复的元素

如果不关心元素在ArrayList中的顺序,可以将list放入set中来删除重复元素后再放回,如果关心顺序则可以用linkedHashSet。

4.有序的Collection

Collection的sort方法可以对list排序,

PriotrityQueue动态的维护一个有序的队列(每次增删都重新排序)

如果Collection中没有重复元素,则treeSet也是一个选择