第11章 集合

Java 集合概述

为了保存数量不确定的数据,以及保存具有映射关系的数据,Java 提供集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有集合类都位于 java.util 包下。集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合里只能保存对象(实际上也是保存对象的引用变量)。
Java的集合类主要由两个接口派生而出:CollectionMapCollectionMap 是 Java 集合框架的根接口,这两个接口又包含了一些子接口和实现类。

总体框架

在这里插入图片描述

Collection 集合体系继承树

在这里插入图片描述

Map 集合体系继承树

在这里插入图片描述
我们可以把 Java 的所有集合分为三大类:

  • Set 集合: Set 集合类似于一个罐子,把一个对象添加到 Set 集合时,Set 集合无法记住添加这个元素的顺序,所以 Set 里的元素不能重复
  • List 集合: List 集合非常像一个数组,它可以记住每次添加元素的顺序
  • Map集合: Map 集合也像一个罐子,只是它里面的每项数据都由两个值组成
    在这里插入图片描述
Collection 和 Iterator 接口

Collection 接口时 List、Set 和 Queue 接口的父接口,Collection 接口中定义的方法可以操作 Set 集合、List 集合以及 Queue 集合。Collection 集合中定义了如下操作集合元素的方法:

  • boolean add(Object o):向集合中添加一个元素
  • Boolean addAll(Collection c):把集合 c 中的元素添加到指定的集合中
  • void clear():清除集合里的所有元素
  • boolean contains(Object o):返回集合里是否包含指定元素
  • boolean containAll(Collection c):返回集合里是否包含集合 c 中的所有元素
  • boolean isEmpty():返回集合是否为空
  • Iterator iterator():返回一个 Iterator 对象,用于遍历集合里的元素
  • boolean remove(Object o):删除集合中指定元素 o
  • boolean removeAll(Collection c):从集合中删除集合 c 中包含的所有元素
  • boolean retainAll(Collection c):从集合中删除集合 c 里不包含的元素
  • int size():该方法返回集合里元素的个数
  • Object[] toArray():该方法把集合转化为一个数组,所有集合元素变为对应数组的元素
  • T[] toArray(T[] a):返回一个数组,该数组包含容器中的所有元素。返回结果的运行时类型与参数数组 a 的类型相同,而不是单纯的 Object。
  1. 注意,其中不包含随机访问所选择元素的 get() 方法。因为 Collection 包括 Set,Set 是自己维护内部顺序的。因此,想要检查 Collection 中的元素,就必须使用迭代器。
使用 Iterator 接口遍历集合元素

Iterator 接口也是 Java 集合框架的一员,Collection、Map 集合主要用于盛装其他对象,而 Iterator 则主要用于遍历 Collection 集合中的元素,Iterator 对象也被称为迭代器。
Iterator 接口里定义了如下三个方法:

  • boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回 true
  • Object next():返回集合里的下一个元素
  • void remove():删除集合里上一次 next 方法返回的元素

如果需要 Iterator 对象,则必须有一个被迭代的集合。

Set 接口

Set 集合不允许包含相同的元素,如果试图把两个相同的元素加入到 Set 中,则添加操作失败。Set 判断两个对象相同不是使用 “==”,而是根据 equals 方法。也就是说如果两个对象用 equals 方法比较返回 true,Set 就不会接受这两个对象;反之,如果两个对象用 equals 方法比较返回 false,Set 就会接受者两个对象。要意识到 Set 需要一种方式来维护存储顺序,而存储顺序如何维护,在不同的 Set 实现之间是会有变化的。

HashSet 类

为快速查找而设计的 Set。HashSet 是 Set 接口的典型实现,HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找。
HashSet 具有以下特点:

  • 不能保证元素的排列顺序,顺序可能会发生变化
  • HashSet 不是同步的,如果多个线程同时访问一个 Set 集合,必须通过代码来保证其同步
  • 集合元素值可以是 null
    当向 HashSet 存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 的值来决定该对象再 HashSet 的存储位置。
    简单地说,HashSet 判断两个元素相等的标准是两个对象通过 equals 方法比较相等,并且两个对象的 hashCode() 方法返回值也相等。

重写 hashCode() 方法的基本规则:
重写 hashCode() 的方式:

TreeSet 类

TreeSet 是 SortedSet 接口的唯一实现,正如 SortedSet 名字所暗示的那样,TreeSet 可以确保集合元素处于排序状态。与前面的 HashSet 集合相比,TreeSet 还提供几个额外的方法:

  • Comparator comparator():返回当前 Set 使用的 Comparator ,或者返回 null,表示以自然方式排序。
  • Object first():返回集合中的第一个元素。
  • Object last():返回集合中的最后一个元素。
  • Object lower():返回集合中位于指定元素之前的元素(即小于指定元素的最大元素)。
  • Object higher():返回集合中位于指定元素之后的元素(即大于指定元素的最大元素)。
  • SortedSet subSet(fromElement, toElement):返回此 Set 的子集合,范围从 fromElement (包含)到 toElement (不包含)。
  • SortedSet headSet(toElement):返回此 Set 的子集,由小于 toElement 的元素组成。
  • SortedSet tailSet(fromElement):返回此 Set 的子集,由大于或等于 fromElement 的元素组成。
package com.chao.chapterElevenCollection;

import java.util.TreeSet;

public class TestTreeSetCommon {
    public static void main(String[] args) {
        TreeSet nums = new TreeSet();
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        //输出集合元素,看到集合元素已经处于排序状态
        System.out.println(nums);
        //输出集合里的第一个元素
        System.out.println(nums.first());
        System.out.println(nums.last());
        //返回小于2的子集,不包含2
        System.out.println(nums.headSet(2));
        System.out.println(nums.tailSet(5));
        //返回大于等于-3,小于4的子集
        System.out.println(nums.subSet(-3, 4));
    }
}

由上面的运行结果可以看出,TreeSet 并不是根据元素的插入顺序进行排序的,而是根据元素的实际值进行排序的。
与 HashSet 集合采用 hash 算法决定元素的存储位置不同,TreeSet 采用红黑树数据结构对元素进行排序。 TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
自然排序
TreeSet 会调用集合当中的 compareTo(Object obj) 方法来比较元素之间的大小,然后将元素按升序排序。
Comparable 接口
Java 提供了一个 Comparable 接口,该接口里定义了一个 compareTo(Object obj) 方法,该方法返回一个数值,实现该接口的类必须实现该方法,实现该接口的类的对象就可以比较大小。当以个对象调用该方法与另一个对象进行比较,例如 obj1.compareTo(obj2),如果该方法返回0,则表明这两个对象相等;如果该方法返回一个正数,则表明 obj1 大于 obj2;如果该方法返回一个负数,则表明 obj1小于 obj2。
下面是实现了 Compareble 接口的常用类:

  • BigDecimal、BigInteger 以及所有数值型对应的包装类:按它们对应的数值的大小进行比较
  • Character:按照字符的 UNICODE 值进行比较
  • Boolean:true 对应的包装类实例大于 false 对应的包装类实例
  • String:按字符串中字符的 UNICODE 值进行比较
  • Data、Time:后面的时间、日期比前面的时间、日期值大

如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Compareble 接口,否则程序将会报异常。

import java.util.TreeSet;

class Err{}

public class TestTreeSetError {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet();
        ts.add(new Err());
        ts.add(new Err());
    }
}

上面程序向 TreeSet 添加两个 Err 对象,添加第一个元素时,TreeSet 中没有任何元素,所以不会出现任何问题;当添加第二个元素时,TreeSet 会调用该对象的 compareTo(Object obj) 方法与集合中的其他对象进行比较,如果对应的类没有实现 Comparable 接口,则会引发 ClassCastException 异常。
还有一点需要指出:只有相同类的两个实例才会比较大小,当试图把一个对象添加到 TreeSet 集合时,TreeSet 会调用对象的 compareTo(Object obj) 方法与集合中其他元素进行比较,这就要求集合中其他元素与该元素是同一个类的实例。也就是说,向 TreeSet 中添加的应该是同一个类的对象,否则也会引发 ClassClassExcepton 异常。

LinkedHashSet 类

具有 HashSet 的查询速度,且内部使用链表维护元素的顺序。

EnumSet 类

List 接口

List 集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素,使用 get() 一次取出一个元素;调用 iterator() 获取用于该序列的 Iterator。

List 接口和 ListIterator 接口

List 作为 Collection 接口的子接口,可以使用 Collection 接口中的所有方法,由于 List 是有序集合,所以 List 集合里增加了一些根据索引来操作集合元素的方法:

  • void add(int index, Object element):将元素 element 插入到 List 集合的 index 处
  • boolean addAll(int index, Collection c):将集合 c 所包含的所有元素都插入到 List 集合的 index 处
  • Object get(index):返回集合 index 索引处的元素
  • int indexOf(Object o):返回对象 o 在 List 集合中最后一次出现的位置索引
  • Object remove(int index):删除并返回 index 索引处的元素
  • Object set(int index, Object element):将 index 索引出的元素替换成 element 对象,返回新元素
  • List subList(int fromIndex, int toIndex):返回索引 fromIndex 到 toIndex (不包含)处集合元素组成的子集合
ArrayList 和 Vector 实现类

ArrayList 和 Vector 作为 List 类的两个典型实现,完全支持前面介绍的 List 接口的全部功能。ArrayList 和 Vector 都是基于***数组***实现的 List 类,所以 ArrayList 和 Vector 类封装了一个动态再分配的 Object[] 数组。

ArrayList 和 Vector 的 capacity 属性
  • 每个 ArrayList 和 Vector 都有一个 capacity 属性,这个属性表示它们所封装的 Object[] 数组的长度。当向 ArrayList 和 Vector 中添加元素时,它们的 capacity 会自动增加。
  • 如果向 ArrayList 或 Vector 中添加大量的对象时,可使用 ensureCapacity 方法一次性地增加 capacity。这样可以减少增加重分配的次数,从而提高性能
  • 如果开始就是到 ArrayList 或 Vector 集合需要保存多少个元素,则可以再创建它们的时候指定 capacity 的大小,capacity 属性的默认大小为 10
  • void ensureCapacity(int minCapacity):将 ArrayList 或 Vector 集合的 capacity 增加 minCapacity
  • void trimToSize():调整ArrayList 或 Vector 集合的 capacity 为列表当前大小

ArrayList 和 Vector 最显著的区别是:ArrayList 是线程不安全的,当多个线程访问同一个 ArrayList 时,如果超过一条线程修改了 ArrayList 集合,则程序必须手动保证该集合的同步性。但 Vector 则是线程安全的。

即使 Vector 时线程安全的,同样不推荐使用 Vector 集合,后面会介绍一个 Collections 工具类,它可以将一个 ArrayList 变为线程安全的。

Stack 类

Vector 还提供了一个 Stack 子类,它用于模拟栈这种数据结构,栈通常指“后进先出”的容器。
Stack 类中提供了如下几个方法:

  • Object peek():返回“栈”中第一个元素,但并不将该元素 “pop” 出 “栈”
  • Object pop():返回“栈”中第一个元素,并将该元素 “pop” 出 “栈”
  • void push(Object item):元素进 “栈”

Queue 接口

Queue 用于模拟队列这种数据结构,队列通常是指“先进先出” 的容器。Queue 接口定义了如下几个方法:

  • void add(Object e):将指定元素加入到此队列的尾部
  • Object element():获取队列头部的元素,但不删除该元素
  • boolean offer(Object e):将指定元素加入此队列的尾部【当使用容量有限制的队列时,此方法通常比 add 要好】
  • Object peek():获取队列头部的元素
  • Object poll():获取队列头部的元素,并删除该元素
  • Object remove():获取队列头部的元素,并删除该元素

Queue 两个常用的实现类:LinkedList 和 PriorityQueue

LinkedList 实现类

LinkedList 类是一个比较奇怪的类,它是 List 接口的实现类,这意味着它是一个 List 集合,可以根据索引来随机访问集合中的元素。除此之外,LinkedList 还实现了 Deque 接口,Deque 接口是 Queue 接口的子接口,它代表一个双向队列,Deque 接口中定义了一些可以双向操作队列的方法:

  • void addFirst(Object e):将指定元素插入到该双向队列的开头

Map

Map 用于保存具有映射关系的数据,因此 Map 集合里保存着两组值。一组值用于保存 Map 里的key,另一组值保存 Map 里的 value,key 和 value 都可以是引用类型的数据。Map 的 key 不允许重复,即同一个 Map 对象的任意两个 key 通过 equals 方法比较总是返回 false。Map 里 key 集和 Set 集合里元素的存储形式也很像,正如它们名字所暗示的 Map 这些实现类和子接口中 key 集存储形式和对应 Set 集合中元素的存储形式完全相同。
如果把 Map 中的 value 放在一起来看,它们又非常类似于一个 List:元素与元素之间可以重复,每个元素可以根据索引来查找,只是 Map 中的索引不再使用整数值,而是以另一个对象来作为索引。Map 接口中定义了如下常用方法:

  • void clear():删除该 Map 对象中所有的 key-value 对
  • boolean containsKey(Object key):查询 Map 中是否包含指定的 key
  • boolean containsValue(Object value):查询 Map 中是否包含一个或多个 value
  • Set entrySet():返回 Map 中所包含的 key-value 所组成的 Set 集合
  • Object get(Object key):返回指定 key 所对应的 value(基本方法)
  • Object put(Object key, Object value):(基本方法)
  • int size():返回 Map 里 key-value 的个数
性能

性能是映射表中的一个重要问题,当 get() 中使用先行搜索时,执行速度会相当慢。HashMap 使用了特殊值,称作 散列码,来取代对键的缓慢搜索。散列值是“相对唯一”的、用以代表对象的 int 值,它是通过将该对象的某些信息进行转换而生成的。HashMap() 就是使用对象的 hashCode() 进行快速查询的,此方法能够显著提升性能。

HashMap 和 Hashtable 实现类

HashMap 和 Hashtable 都是 Map 接口的典型实现类,Hashtable 是一个古老的 Map 实现类。HashMap 和 Hashtable 存在两点典型的区别:

  • Hashtable 是一个线程安全的 Map 实现,但 HashMap 是线程不安全的实现,所以 HashMap 比 Hashtable 的性能高一些;但如果有多个线程访问同一个 Map 对象时,使用 Hashtable 好一些。
  • Hashtable 不允许 null 作为 key 和 value ;HashMap 可以使用 null 作为 key 和 value。
SortedMap 类

使用 SortedMap (TreeMap 是其现阶段的唯一实现类),可以确保键处于排序状态。这使得它具有额外的功能。

TreeMap 实现类

与 TreeSet 类似的是,TreeMap 也是基于红黑树对 TreeMap 中所有 key 进行排序,从而保证 TreeMap 中所有 key-value 对处于有序状态。TreeMap 也有两种排序方式:

  • 自然排序:
  • 定制排序:
    TreeMap 也提供了系列根据 key 顺序来访问 Map 中 key-value 的方法:
LinkedHashMap 实现类

为了提高速度,LinkedHashMap 散列化所有元素,但是在对键值对遍历时,却又以元素的插入顺序返回键值对。

WeakHashMap 实现类
ConcurrentHashMap 实现类
EnumMap 实现类

散列与散列码

操作集合的工具类 Collections

Java 提供了一个操作Set、List 和 Map 的工具类:Collections,该工具类提供大量的方法对集合元素进行排序、查询和修改等操作,还提供了将集合对象设置为不可变、对集合实现同步控制等方法。

排序操作

Collections 提供如下方法对 List 集合元素进行排序:

  • static void reverse(List list):反转指定 List 集合中元素的顺序
  • static void shuffle(List list):对 List 集合元素进行随机排序
  • static void sort(List list):根据元素的自然顺序对指定 List 集合的元素按升序进行排序
  • static void sort(List list, Comparator c):根据指定 Comparator 产生的顺序对 List 集合元素进行排序
  • static void swap(List list, int i, int j):将指定 List 集合中 i 处元素和 j 处元素进行交换
  • static void rotate(List list, int distance):将指定集合中 i 处元素和 list.length-1-i 处元素进行交换

集合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值