day11 11 集合总结

11、集合总结

在编程时,可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的。如果需要保存数量变化的数据,数组就有点无能为力了。而且数组无法保存具有映射关系的数据,如成绩表为语文——79,数学——80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。

为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。

集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。

11.1 集合的分类

Java 集合类型分为 Collection 和 Map,下面用两张图来表示对应的分类

黄色块为集合的接口,蓝色块为集合的实现类。

11.1.1 Collection

在这里插入图片描述

接口名称作 用
Iterator 接口集合的输出接口,主要用于遍历输出(即迭代访问)Collection 集合中的元素,Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口,实现类实现 Collection 时就必须实现 Iterator 接口。
Collection 接口是 List、Set 和 Queue 的父接口,是存放一组单值的最大接口。所谓的单值是指集合中的每个元素都是一个对象。一般很少直接使用此接口直接操作。
Queue 接口Queue 是 Java 提供的队列实现,有点类似于 List。
Dueue 接口是 Queue 的一个子接口,为双向队列。
List 接口是最常用的接口。是有序集合,允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置,用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,与数组类似。
Set 接口不能包含重复的元素。
Map 接口是存放一对值的最大接口,即接口中的每个元素都是一对,以 key➡value 的形式保存。

11.1.2 Map

在这里插入图片描述

对于 Set、List、Queue 和 Map 这 4 种集合,Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。我们先介绍这些实现类

类名称作用
HashSet为优化査询速度而设计的 Set。它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,实现比较简单
TreeSet实现了 Set 接口,是一个有序的 Set,这样就能从 Set 里面提取一个有序序列
ArrayList一个用数组实现的 List,能进行快速的随机访问,效率高而且实现了可变大小的数组
ArrayDueue是一个基于数组实现的双端队列,按“先进先出”的方式操作集合元素
LinkedList对顺序访问进行了优化,但随机访问的速度相对较慢。此外它还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它当成栈(Stack)或队列(Queue)
TreeMap可以对键对象进行排序

11.2 Collection接口介绍

简介
Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。Collection 是接口,不能对其实例化
通用集合方法

方法名称说明
boolean add(E e)向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型
boolean addAll(Collection c)向集合中添加集合 c 中的所有元素,如果集合对象被添加操作改变了,则返回 true。
void clear()清除集合中的所有元素,将集合长度变为 0。
boolean contains(Object o)判断集合中是否存在指定元素
boolean containsAll(Collection c)判断集合中是否包含集合 c 中的所有元素
boolean isEmpty()判断集合是否为空
Iteratoriterator()返回一个 Iterator 对象,用于遍历集合中的元素
boolean remove(Object o)从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。
boolean removeAll(Collection c)从集合中删除所有在集合 c 中出现的元素(相当于把调用该方法的集合减去集合 c)。如果该操作改变了调用该方法的集合,则该方法返回 true。
boolean retainAll(Collection c)从集合中删除集合 c 里不包含的元素(相当于把调用该方法的集合变成该集合和集合 c 的交集),如果该操作改变了调用该方法的集合,则该方法返回 true。
int size()返回集合中元素的个数
Object[] toArray()把集合转换为一个数组,所有的集合元素变成对应的数组元素。

11.3 List集合(LinkedList,ArrayList,Vector)

11.3.1 介绍

List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。

List 实现了 Collection 接口,在List中最常用的两个类就数ArrayList和LinkedList。他们两个的实现代表着数据结构中的两种典型:线性表和链表。在这里,这个线性表是可以根据需要自动增长的。Java的库里面默认没有实现单链表,LinkedList实际上是一个双链表。

11.3.2 ArrayList

1)介绍
ArrayList是内部基于数组的线性表实现,使用transient Object[] elementData; // non-private to simplify nested class access数组变量来保存元素,使用private int size;来保存当前list的长度。

ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。
2)构造方法
ArrayList 类的常用构造方法有如下形式:

  • ArrayList() 构造一个初始容量为10的空列表。
  • ArrayList(Collection<? extends E> c) 构造一个列表,其中包含指定集合的元素,按集合的迭代器返回元素的顺序排列。
  • ArrayList(int initialCapacity) 构造具有指定初始容量的空列表。

3)常用方法(增加、删除、其他)
3.1 增加元素

  • boolean add(E e) 将指定的元素附加到此列表的末尾。
  • void add(int index, E element) 将指定元素插入到列表中的指定位置。
    +boolean addAll(Collection<? extends E> c) 将指定集合中的所有元素按照指定集合的迭代器返回它们的顺序,追加到此列表的末尾。
  • boolean addAll(int index, Collection<? extends E> c) 从指定位置开始,将指定集合中的所有元素插入此列表。

3.2)删除元素

  • public E remove(int index) :删除单个元素
  • public boolean remove(Object o) :删除单个元素
  • protected void removeRange(int fromIndex, int toIndex):删除区段内元素
  • public boolean removeAll(Collection<?> c):删除集合中的共同元素
  • public void clear():删除所有元素

3.3)其他常用方法

  • E get(int index) 获取此集合中指定索引位置的元素,E 为集合中元素的数据类型
  • int index(Object o) 返回此集合中第一次出现指定元素的索引,如果此集合不包含该元
    素,则返回 -1
  • int lastIndexOf(Object o) 返回此集合中最后一次出现指定元素的索引,如果此集合不包含该
    元素,则返回 -1
  • E set(int index, Eelement) 将此集合中指定索引位置的元素修改为 element 参数指定的对象。
    此方法返回此集合中指定索引位置的原元素
  • List subList(int fromlndex, int tolndex) 返回一个新的集合,新集合中包含 fromlndex 和 tolndex 索引之间的所有元素。包含 fromlndex 处的元素,不包含 tolndex 索引处的
    元素

11.3.3 LinkedList

1)介绍
LinkedList 同时实现了List< E >Deque< E >两个接口,所以也满足先进先出的队列特性。此外LinkedList还提供了栈的特性。
LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。

2)构造方法

  • LinkedList()
    构造一个空列表。
  • LinkedList(Collection<? extends E> c)
    构造一个列表,其中包含指定集合的元素,按集合的迭代器返回元素的顺序排列。

3)常用方法
LinkedList 类除了包含 Collection 接口和 List 接口中的所有方法之外,还特别提供了一下方法。

方法名称说明
void addFirst(E e)将指定元素添加到此集合的开头
void addLast(E e)将指定元素添加到此集合的末尾
E getFirst()返回此集合的第一个元素
E getLast()返回此集合的最后一个元素
E removeFirst()删除此集合中的第一个元素
E removeLast()删除此集合中的最后一个元素

4)与ArrayList的区别
ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。

5)栈的特性
另外,LinkedList还有一个有意思的特性,它本身也实现了一个栈的规范。它采用每次直接在链表头之前添加元素来实现push方法,删除链表头元素实现pop方法。和栈实现相关的方法实现如下:

public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
 }
public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
}
public void push(E e) {
        addFirst(e);
}
public E pop() {
        return removeFirst();
}

这样,以后我们如果要使用栈的话,除了声明Stack类以外,把LinkedList当成Stack使也是可行的。

11.3.4 Vector

1)简介
Vector类实现了一个可增长的对象数组。与数组一样,它包含可以使用整数索引访问的组件。
但是,向量的大小可以根据需要增长或收缩,以适应在创建向量之后添加和删除项。每个向量都试图通过维护容量和电容增量来优化存储管理。容量总是至少与向量大小相同;它通常更大,因为随着组件被添加到向量中,向量的存储以块的形式增加应用程序可以在插入大量组件之前增加向量的容量;
这减少了增量重新分配的数量。

从Java 2平台v1.2开始,对该类进行了改进以实现List接口,使其成为Java集合框架的成员。
与新的集合实现不同,Vector是同步的。

如果不需要线程安全的实现,建议使用ArrayList代替Vector。

2)构造方法

  • Vector()
    构造一个空向量,使其内部数据数组大小为10,标准容量增量为0。
  • Vector(Collection<? extends E> c)
    构造一个空向量,使其内部数据数组大小为10,标准容量增量为0。
  • Vector(int initialCapacity)
    构造一个具有指定初始容量且其容量增量为零的空向量。
  • Vector(int initialCapacity, int capacityIncrement)
    构造具有指定初始容量和容量增量的空向量。

3)常用方法
与ArrayList的方法用法非常相似

11.4 Set类(HashSet 类和 TreeSet类)

11.4.1 简介

Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素

Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类。

11.4.2 HashSet类

1)简介
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。

HashSet 具有以下特点:

  • 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
  • HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
  • 集合元素值可以是 null。

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。
注意:如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。
2)构造方法

  • HashSet()
    构造一个新的空集;支持的HashMap实例具有默认的初始容量(16)和负载因子(0.75)。
  • HashSet(Collection<? extends E> c)
    构造一个新集合,该集合包含指定集合中的元素。
  • HashSet(int initialCapacity)
    构造一个新的空集;支持的HashMap实例具有指定的初始容量和缺省负载因子(0.75)。
  • HashSet(int initialCapacity, float loadFactor)
    构造一个新的空集;支持的HashMap实例具有指定的初始容量和指定的负载因子。

3)常用方法
在 HashSet 类中实现了 Collection 接口中的所有方法。常用的就是Collection接口里的方法。

11.4.3 TreeSet类

1)简介
TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。

TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。

2)实现Comparable接口类对象的比较方式

比较方式
包装类(BigDecimal、Biglnteger、 Byte、Double、Float、Integer、Long 及 Short)按数字大小比较
Character按字符的 Unicode 值的数字大小比较
String按字符串中字符的 Unicode 值的数字大小比较

3)构造方法

  • TreeSet()
    构造一个新的空树集,根据其元素的自然顺序排序。
  • TreeSet(Collection<? extends E> c)
    构造一个新树集,其中包含指定集合中的元素,按照其元素的自然顺序排序。
  • TreeSet(Comparator<? super E> comparator)
    构造一个新的空树集,根据指定的比较器排序。
  • TreeSet(SortedSet s)
    构造一个包含相同元素的新树集,并使用与指定的已排序集相同的顺序。

4)常用方法
TreeSet 类除了实现 Collection 接口的所有方法之外,还提供如下的方法。

方法名称说明
E first()返回此集合中的第一个元素。其中,E 表示集合中元素的数据类型
E last()返回此集合中的最后一个元素
E poolFirst()获取并移除此集合中的第一个元素
E poolLast()获取并移除此集合中的最后一个元素
SortedSet subSet(E fromElement,E toElement)返回一个新的集合,新集合包含原集合中 fromElement 对象与 toElement对象之间的所有对象。包含 fromElement 对象,不包含 toElement 对象
SortedSet headSet<E toElement〉返回一个新的集合,新集合包含原集合中 toElement 对象之前的所有对象。不包含 toElement 对象
SortedSet tailSet(E fromElement)返回一个新的集合,新集合包含原集合中 fromElement 对象之后的所有对象。包含 fromElement 对象

5)注意
在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常。如果向 TreeSet 集合中添加了一个 Double 类型的对象,则后面只能添加 Double 对象,不能再添加其他类型的对象,例如 String 对象等。

11.5 Map集合及其遍历

11.5.1 简介

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。

Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。

Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。
Map接口实现的方法

方法名称说明
void clear()删除该 Map 对象中的所有 key-value 对。
boolean containsKey(Object key)查询 Map 中是否包含指定的 key,如果包含则返回 true。
boolean containsValue(Object value)查询 Map 中是否包含一个或多个 value,如果包含则返回 true。
V get(Object key)返回 Map 集合中指定键对象所对应的值。V 表示值的数据类型V put(K key, V value) 向 Map 集合中添加键-值对,如果当前 Map 中已有一个与该 key 相等的 key-value 对,则新的 key-value 对会覆盖原来的 key-value 对。
void putAll(Map m)将指定 Map 中的 key-value 对复制到本 Map 中。
V remove(Object key)从 Map 集合中删除 key 对应的键-值对,返回 key 对应的 value,如果该 key 不存在,则返回 null
boolean remove(Object key, Object value)这是 Java 8 新增的方法,删除指定 key、value 所对应的 key-value 对。如果从该 Map 中成功地删除该 key-value 对,该方法返回 true,否则返回 false。
Set entrySet()返回 Map 集合中所有键-值对的 Set 集合,此 Set 集合中元素的数据类型为 Map.Entry
Set keySet()返回 Map 集合中所有键对象的 Set 集合
boolean isEmpty()查询该 Map 是否为空(即不包含任何 key-value 对),如果为空则返回 true。
int size()返回该 Map 里 key-value 对的个数
Collection values()返回该 Map 里所有 value 组成的 Collection

11.5.2 HashMap类

1)简介
基于哈希表实现的映射接口。此实现提供所有可选的映射操作,并允许空值和空键。(HashMap类大致相当于Hashtable,只是它是不同步的,并且允许为空。)该类不保证映射的顺序;特别是,它不能保证订单在一段时间内保持不变。

HashMap实例有两个影响其性能的参数:初始容量和负载因子。
容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。负载因子是一个度量哈希表在其容量自动增加之前允许的满度的度量。当哈希表中的条目数超过负载因子和当前容量的乘积时,将对哈希表进行重新哈希(即重新构建内部数据结构),使哈希表的桶数大约是桶数的两倍。一般来说,默认的负载因子(.75)在时间和空间成本之间提供了很好的权衡。更高的值减少了空间开销,但增加了查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置map的初始容量时,应该考虑map中条目的期望数量及其负载因子,从而最小化rehash操作的数量。

注意,使用具有相同hashCode()的多个键肯定会降低任何散列表的性能。为了改善影响,当键是可比较的,这个类可以使用键之间的比较顺序来帮助打破联系。

注意,这个实现不是同步的。如果多个线程同时访问一个散列映射,并且至少有一个线程从结构上修改了该映射,则必须在外部对其进行同步。

2)构造函数

  • HashMap()
    使用默认初始容量(16)和默认负载因子(0.75)构造一个空HashMap。
  • HashMap(int initialCapacity)
    构造一个具有指定初始容量和缺省负载因子(0.75)的空HashMap。
  • HashMap(int initialCapacity, float loadFactor)
    构造具有指定初始容量和负载因子的空HashMap。
  • HashMap(Map<? extends K,? extends V> m)
    使用与指定映射相同的映射构造新的HashMap。

3)常用方法
参考java8开发手册

11.5.3 TreeMap类

1)简介
一个基于红黑树的NavigableMap实现。映射根据其键的自然顺序进行排序,或者根据使用的构造函数由创建映射时提供的比较器进行排序。这个实现为containsKey、get、put和remove操作提供了保证的log(n)时间成本。算法是对Cormen, Leiserson和Rivest介绍的算法的改编。

注意,这个实现不是同步的。
如果多个线程同时访问一个映射,并且至少有一个线程在结构上修改了映射,那么它必须在外部同步。(结构修改是指任何增加或删除一个或多个映射的操作;仅更改与现有键关联的值不是结构修改)。这通常是通过在一些自然封装了映射的对象上进行同步来实现的。如果不存在这样的对象,则应该使用集合“包装”映射。

因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。

注意,不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。故障快速迭代器在最大努力的基础上抛出ConcurrentModificationException。
因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。
注意,可以使用put更改关联映射中的映射。
2)构造方法

  • TreeMap()
    使用键的自然顺序构造一个新的空树映射。
  • TreeMap(Comparator<? super K> comparator)
    构造一个新的、空的树映射,根据给定的比较器排序。
  • TreeMap(Map<? extends K,? extends V> m)
    构造一个新的树映射,其中包含与给定映射相同的映射,并根据其键的自然顺序排序。
  • TreeMap(SortedMap<K,? extends V> m)
    构造一个包含相同映射的新树映射,并使用与指定排序映射相同的顺序。

3)常用方法
参考java开发手册

11.5.4 Map集合的遍历(4种方法)

Map 集合的遍历与 List 和 Set 集合不同。Map 有两组值,因此遍历时可以只遍历值的集合,也可以只遍历键的集合,也可以同时遍历。Map 以及实现 Map 的接口类(如 HashMap、TreeMap、LinkedHashMap、Hashtable 等)都可以用以下几种方式遍历。

1)在 for 循环中使用 entries 实现 Map 的遍历(最常见和最常用的)。

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("入门教程1", "java");//使用put修改映射
    map.put("入门教程2", "c");
    for (Map.Entry<String, String> entry : map.entrySet()) {
        String mapKey = entry.getKey();
        String mapValue = entry.getValue();
        System.out.println(mapKey + ":" + mapValue);
    }
}

2)使用 for-each 循环遍历 key 或者 values,一般适用于只需要 Map 中的 key 或者 value 时使用。性能上比 entrySet 较好。

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("入门教程1", "java");//使用put修改映射
    map.put("入门教程2", "c");
	// 打印键集合
	for (String key : map.keySet()) {
  	  	System.out.println(key);
	}
	// 打印值集合
	for (String value : map.values()) {
    	System.out.println(value);
	}
}

3)使用迭代器(Iterator)遍历

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("入门教程1", "java");//使用put修改映射
    map.put("入门教程2", "c");
	Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
    Entry<String, String> entry = entries.next();
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key + ":" + value);
}
}

4)通过键找值遍历,这种方式的效率比较低,因为本身从键取值是耗时的操作。

for(String key : map.keySet()){
    String value = map.get(key);
    System.out.println(key+":"+value);
}

11.5.5 java8中 Map新增方法

Java 8 除了为 Map 增加了 remove(Object key, Object value) 默认方法之外,还增加了如下方法。

名称说明
Object compute(Object key, BiFunction remappingFunction)该方法使用 remappingFunction 根据原 key-value 对计算一个新 value。只要新 value 不为 null,就使用新 value 覆盖原 value;如果原 value 不为 null,但新 value 为 null,则删除原 key-value 对;如果原 value、新 value 同时为 null,那么该方法不改变任何 key-value 对,直接返回 null。
Object computeIfAbsent(Object key, Function mappingFunction)如果传给该方法的 key 参数在 Map 中对应的 value 为 null,则使用 mappingFunction 根据 key 计算一个新的结果,如果计算结果不为 null,则用计算结果覆盖原有的 value。如果原 Map 原来不包括该 key,那么该方法可能会添加一组 key-value 对。
Object computeIfPresent(Object key, BiFunction remappingFunction)如果传给该方法的 key 参数在 Map 中对应的 value 不为 null,该方法将使用 remappingFunction 根据原 key、value 计算一个新的结果,如果计算结果不为 null,则使用该结果覆盖原来的 value;如果计算结果为 null,则删除原 key-value 对。
void forEach(BiConsumer action)该方法是 Java 8 为 Map 新增的一个遍历 key-value 对的方法,通过该方法可以更简洁地遍历 Map 的 key-value 对。
Object getOrDefault(Object key, V defaultValue)获取指定 key 对应的 value。如果该 key 不存在,则返回 defaultValue。
Object merge(Object key, Object value, BiFunction remappingFunction)该方法会先根据 key 参数获取该 Map 中对应的 value。如果获取的 value 为 null,则直接用传入的 value 覆盖原有的 value(在这种情况下,可能要添加一组 key-value 对);如果获取的 value 不为 null,则使用 remappingFunction 函数根据原 value、新 value 计算一个新的结果,并用得到的结果去覆盖原有的 value。
Object putIfAbsent(Object key, Object value)该方法会自动检测指定 key 对应的 value 是否为 null,如果该 key 对应的 value 为 null,该方法将会用新 value 代替原来的 null 值。
Object replace(Object key, Object value)将 Map 中指定 key 对应的 value 替换成新 value。与传统 put() 方法不同的是,该方法不可能添加新的 key-value 对。如果尝试替换的 key 在原 Map 中不存在,该方法不会添加 key-value 对,而是返回 null。
boolean replace(K key, V oldValue, V newValue)将 Map 中指定 key-value 对的原 value 替换成新 value。如果在 Map 中找到指定的 key-value 对,则执行替换并返回 true,否则返回 false。
replaceAll(BiFunction function)该方法使用 BiFunction 对原 key-value 对执行计算,并将计算结果作为该 key-value 对的 value 值。

11.5.6 如何创建不可变集合(of(),Map.Entry()显示使用)

Java 9 版本以前,假如要创建一个包含 6 个元素的 Set 集合,程序需要先创建 Set 集合,然后调用 6 次 add() 方法向 Set 集合中添加元素。Java 9 对此进行了简化,程序直接调用 Set、List、Map 的 of() 方法即可创建包含 N 个元素的不可变集合,这样一行代码就可创建包含 N 个元素的集合。
创建不可变的 Map 集合有两个方法。使用 of() 方法时只要依次传入多个 key-value 对即可;还可使用 ofEntries() 方法,该方法可接受多个 Entry 对象,因此程序显式使用 Map.entry() 方法来创建 Map.Entry 对象。
Test


		// 创建包含4个元素的Set集合
		Set set1 = Set.add("1").add("2").add("3").add("4");
        Set set2 = Set.of("1", "2", "3", "4");
    
       // 使用Map.entry()方法显式构建key-value对
       Map map = Map.ofEntries(Map.entry("语文", 89), Map.entry("数学", 82), Map.entry("英语", 92));

11.6 Collections工具类

11.6.1 简介

Collections 类是 Java 提供的一个操作 Set、List 和 Map 等集合的工具类。Collections 类提供了许多操作集合的静态方法,借助这些静态方法可以实现集合元素的排序、查找替换和复制等操作。下面介绍 Collections 类中操作集合的常用方法。

11.6.2 常用方法

1)排序(正向和逆向)
Collections 提供了如下方法用于对 List 集合元素进行排序。

  • void reverse(List list):对指定 List 集合元素进行逆向排序。
  • void shuffle(List list):对 List 集合元素进行随机排序(shuffle 方法模拟了“洗牌”动作)。
  • void sort(List list):根据元素的自然顺序对指定 List 集合的元素按升序进行排序。
  • void sort(List list, Comparator c):根据指定 Comparator 产生的顺序对 List 集合元素进行排序。
  • void swap(List list, int i, int j):将指定 List 集合中的 i 处元素和 j 处元素进行交换。
  • void rotate(List list, int distance):当 distance 为正数时,将 list 集合的后 distance 个元素“整体”移到前面;当 distance 为负数时,将 list 集合的前 distance 个元素“整体”移到后面。该方法不会改变集合的长度

例子

public class Test1 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        List prices = new ArrayList();
        for (int i = 0; i < 5; i++) {
            System.out.println("请输入第 " + (i + 1) + " 个商品的价格:");
            int p = input.nextInt();
            prices.add(Integer.valueOf(p)); // 将录入的价格保存到List集合中
        }
        Collections.sort(prices); // 调用sort()方法对集合进行排序,因为是静态方法,使用类名访问
        System.out.println("价格从低到高的排列为:");
        for (int i = 0; i < prices.size(); i++) {
            System.out.print(prices.get(i) + "\t");
        }
    }
}

2)查找、替换操作
Collections 还提供了如下常用的用于查找、替换集合元素的方法。

  • int binarySearch(List list, Object key):使用二分搜索法搜索指定的 List 集合,以获得指定对象在 List 集合中的索引。如果要使该方法可以正常工作,则必须保证 List 中的元素已经处于有序状态。
  • Object max(Collection coll):根据元素的自然顺序,返回给定集合中的最大元素。
  • Object max(Collection coll, Comparator comp):根据 Comparator 指定的顺序,返回给定集合中的最大元素。
  • Object min(Collection coll):根据元素的自然顺序,返回给定集合中的最小元素。
  • Object min(Collection coll, Comparator comp):根据 Comparator 指定的顺序,返回给定集合中的最小元素。
  • void fill(List list, Object obj):使用指定元素 obj 替换指定 List 集合中的所有元素。
  • int frequency(Collection c, Object o):返回指定集合中指定元素的出现次数。
  • int indexOfSubList(List source, List target):返回子 List 对象在父 List 对象中第一次出现的位置索引;如果父 List 中没有出现这样的子 List,则返回 -1。
  • int lastIndexOfSubList(List source, List target):返回子 List 对象在父 List 对象中最后一次出现的位置索引;如果父 List 中没有岀现这样的子 List,则返回 -1。
  • boolean replaceAll(List list, Object oldVal, Object newVal):使用一个新值 newVal 替换 List 对象的所有旧值 oldVal。

3)复制
Collections 类的 copy() 静态方法用于将指定集合中的所有元素复制到另一个集合中。执行 copy() 方法后,目标集合中每个已复制元素的索引将等同于源集合中该元素的索引。
copy() 方法的语法格式如下:

void copy(List <? super T> dest,List<? extends T> src)

其中,dest 表示目标集合对象,src 表示源集合对象。

11.6.3 遍历Collection的方法(4种)

1)使用 Lambda 表达式来遍历集合元素
Java 8 为 Iterable 接口新增了一个 forEach(Consumer action) 默认方法,该方法所需参数的类型是一个函数式接口,而 Iterable 接口是 Collection 接口的父接口,因此 Collection 集合也可直接调用该方法。

当程序调用 Iterable 的 forEach(Consumer action) 遍历集合元素时,程序会依次将集合元素传给 Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。正因为 Consumer 是函数式接口,因此可以使用 Lambda 表达式来遍历集合元素。

如下程序示范了使用 Lambda 表达式来遍历集合元素。

public class CollectionEach {
    public static void main(String[] args) {
        // 创建一个集合
        Collection objs = new HashSet();
        objs.add("1");
        objs.add("2");
        objs.add("3");//add是Collection接口的通用方法
        // 调用forEach()方法遍历集合
        objs.forEach(obj -> System.out.println("迭代集合元素:" + obj));
    }
}

2)通过迭代器遍历Collection集合的元素
Iterator(迭代器)是一个接口,它的作用就是遍历容器的所有元素,也是 Java 集合框架的成员,但它与 Collection 和 Map 系列的集合不一样,Collection 和 Map 系列集合主要用于盛装其他对象,而 Iterator 则主要用于遍历(即迭代访问)Collection 集合中的元素。

Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口。Iterator 接口里定义了如下 4 个方法。

  • boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。
  • Object next():返回集合里的下一个元素。
  • void remove():删除集合里上一次 next 方法返回的元素。
  • void forEachRemaining(Consumer action):这是 Java 8 为 Iterator 新增的默认方法,该方法可使用 Lambda 表达式来遍历集合元素。
	 Iterator it = objs.iterator();
        while (it.hasNext()) {
            // it.next()方法返回的数据类型是Object类型,因此需要强制类型转换
            String obj = (String) it.next();
            System.out.println(obj);
            }

Iterator 必须依附于 Collection 对象,若有一个 Iterator 对象,则必然有一个与之关联的 Collection 对象。Iterator 提供了两个方法来迭代访问 Collection 集合里的元素,并可通过 remove() 方法来删除集合中上一次 next() 方法返回的集合元素。

3)使用Lambda表达式遍历Iterator迭代器
Java 8 为 Iterator 引入了一个 forEachRemaining(Consumer action) 默认方法,该方法所需的 Consumer 参数同样也是函数式接口。当程序调用 Iterator 的 forEachRemaining(Consumer action) 遍历集合元素时,程序会依次将集合元素传给 Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。

 // 获取objs集合对应的迭代器
        Iterator it = objs.iterator();
        // 使用Lambda表达式(目标类型是Comsumer)来遍历集合元素
        it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));

调用了 Iterator 的 forEachRemaining() 方法来遍历集合元素,传给该方法的参数是一个 Lambda 表达式,该 Lambda 表达式的目标类型是 Consumer,因此上面代码也可用于遍历集合元素。

4)使用foreach循环遍历Collection集合
我们还可以使用 Java 5 提供的 foreach 循环迭代访问集合元素,而且更加便捷。

	for (Object obj : objs) {
            // 此处的obj变量也不是集合元素本身
            String obj1 = (String) obj;
            System.out.println(obj1);
            }

注意只要迭代访问集合元素时,该集合不能被改变,否则将引发 ConcurrentModificationException 异常。上面4种方式都不能改变集合的元素

11.6.4 使用Java 8新增的Predicate操作Collection集合

Java 8 起为 Collection 集合新增了一个 removeIf(Predicate filter) 方法,该方法将会批量删除符合 filter 条件的所有元素。该方法需要一个 Predicate 对象作为参数,Predicate 也是函数式接口,因此可使用 Lambda 表达式作为参数。
依然是最上面的那个程序

public class CollectionEach {
    public static void main(String[] args) {
        // 创建一个集合
        Collection objs = new HashSet();
        objs.add("1fsgs");
        objs.add("2dsdgdgs");
        objs.add("3dgsdgsdfg");//add是Collection接口的通用方法
 // 使用Lambda表达式(目标类型是Predicate)过滤集合
        objs.removeIf(ele -> ((String) ele).length() < 12);
        //程序传入一个 Lambda 表达式作为过滤条件。所有长度小于 12 的字符串元素都会被删除。
        System.out.println(objs);
   }

11.6.5 使用Java 8新增的Stream操作Collection集合

1)简介
Java 8 新增了 Stream、IntStream、LongStream、DoubleStream 等流式 API,这些 API 代表多个支持串行和并行聚集操作的元素。上面 4 个接口中,Stream 是一个通用的流接口,而 IntStream、LongStream、 DoubleStream 则代表元素类型为 int、long、double 的流。

Java 8 还为上面每个流式 API 提供了对应的 Builder,例如 Stream.Builder、IntStream.Builder、LongStream.Builder、DoubleStream.Builder,开发者可以通过这些 Builder 来创建对应的流。

独立使用 Stream 的步骤如下:

  1. 使用 Stream 或 XxxStream 的 builder() 类方法创建该 Stream 对应的 Builder。
  2. 重复调用 Builder 的 add() 方法向该流中添加多个元素。
  3. 调用 Builder 的 build() 方法获取对应的 Stream。
  4. 调用 Stream 的聚集方法。

2)Test

package collectionandmap;
/**
 * 目的:演示Stream的使用
 *
 * 独立使用 Stream 的步骤如下:
 * 使用 Stream 或 XxxStream 的 builder() 类方法创建该 Stream 对应的 Builder。
 * 重复调用 Builder 的 add() 方法向该流中添加多个元素。
 * 调用 Builder 的 build() 方法获取对应的 Stream。
 * 调用 Stream 的聚集方法。
 *
 * 注意事项:
 * Stream 提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是 "末端的"(terminal)。
 * 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的 map() 方法就是中间方法。中间方法的返回值是另外一个流。
 * 末端方法:末端方法是对流的最终操作。当对某个 Stream 执行末端方法后,该流将会被“消耗”且不再可用。 sum()、count()、average() 等方法都是末端方法。
 */


import java.util.stream.IntStream;

public class IntStreamTest {
        public static void main(String[] args) {
            IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).build();
            // 下面调用聚集方法的代码每次只能执行一行,都是末端方法
            // System.out.println("is 所有元素的最大值:" + is.max().getAsInt());
            //            System.out.println("is 所有元素的最小值:" + is.min().getAsInt());
            //            System.out.println("is 所有元素的总和:" + is.sum());
            //            System.out.println("is 所有元素的总数:" + is.count());
            //            System.out.println("is 所有元素的平均值:" + is.average());
            //            System.out.println("is所有元素的平方是否都大于20: " + is.allMatch(ele -> ele * ele > 20));
            //            System.out.println("is是否包含任何元素的平方大于20 : " + is.anyMatch(ele -> ele * ele > 20));



            // 将is映射成一个新Stream,新Stream的每个元素是原Stream元素的2倍+1,中端方法
            IntStream newIs = is.map(ele -> ele * 2 + 1);
            // 使用方法引用的方式来遍历集合元素
            newIs.forEach(System.out::println); // 输岀 41 27 -3 37
        }
    }

在这里插入图片描述

Stream 提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是 “末端的”(terminal)。

  • 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的 map() 方法就是中间方法。中间方法的返回值是另外一个流。
  • 末端方法:末端方法是对流的最终操作。当对某个 Stream 执行末端方法后,该流将会被“消耗”且不再可用。上面程序中的 sum()、count()、average() 等方法都是末端方法。

除此之外,关于流的方法还有如下两个特征。

  • 有状态的方法:这种方法会给流增加一些新的属性,比如元素的唯一性、元素的最大数量、保证元素以排序的方式被处理等。有状态的方法往往需要更大的性能开销。
  • 短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素。

3)Stream常用中间方法

方法说明
filter(Predicate predicate)过滤 Stream 中所有不符合 predicate 的元素
mapToXxx(ToXxxFunction mapper)使用 ToXxxFunction 对流中的元素执行一对一的转换,该方法返回的新流中包含了 ToXxxFunction 转换生成的所有元素。
peek(Consumer action)依次对每个元素执行一些操作,该方法返回的流与原有流包含相同的元素。该方法主要用于调试。
distinct()该方法用于排序流中所有重复的元素(判断元素重复的标准是使用 equals() 比较返回 true)。这是一个有状态的方法。
sorted()该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法。
limit(long maxSize)该方法用于保证对该流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法。

4)Stream 常用的末端方法。

方法说明
forEach(Consumer action)遍历流中所有元素,对每个元素执行action
toArray()将流中所有元素转换为一个数组
reduce()该方法有三个重载的版本,都用于通过某种操作来合并流中的元素
min()返回流中所有元素的最小值
max()返回流中所有元素的最大值
count()返回流中所有元素的数量
anyMatch(Predicate predicate)判断流中是否至少包含一个元素符合 Predicate 条件。
allMatch(Predicate predicate)判断流中是否每个元素都符合 Predicate 条件
noneMatch(Predicate predicate)判断流中是否所有元素都不符合 Predicate 条件
findFirst()返回流中的第一个元素
findAny()返回流中的任意一个元素
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨夜※繁华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值