《疯狂java讲义》第8章 java集合

第8章 java集合

Java集合类是一种特别有用的工具类,可用于存储数量不等的对象,并可以实现常用的数据结构,如队列等。除此之外,Java集合还可用于保存具有映射关系关联数组。Java集合大致可分为SetListQueueMap四种体系,其中Set代表无序、不可重复的集合;List代表有序、重复的集合;而Map则代表具有映射关系的集合,Java5又增加了Queue体系集合,代表一种队列集合实现。

8.1 java集合概述

  • 集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。(所有的集合类都位于java.util包下,处理多线程环境下的并发安全问题,Java5还在java.util.concurrent包下提供了一些多线程支持的集合类。)
  • Java的集合类主要由两个接口派生而出:CollectionMap,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
  • Collection集合:
    在这里插入图片描述
  • Map保存的每项数据都是 key-value 对(根据key来获取数据)
    在这里插入图片描述

Set代表无序、不可重复的集合;
List代表有序、重复的集合;
Map则代表具有映射关系(key-value)的集合;
Queue体系集合,代表一种队列集合实现。

  • 常用的实现类:HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap、TreeMap 等。

8.2 Collection和Iterator接口

  • Collection接口定义的如下操作集合元素的方法:

boolean add(Object o):该方法用于向集合里添加一个元素。
boolean addAll(Collection c):该方法把集合c里的所有元素添加到指定集合里。
void clear()清除集合里的所有元素,将集合长度变为0。
boolean contains(Objecto):返回集合里是否包含指定元素。
boolean containsAll(Collection c):返回集合里是否包含集合c里的所有元素。
boolean isEmpty():返回集合是否为空
Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。
boolean remove(Object o)删除集合中的指定元素o,(当包含了一个或多个元素o时,只删除第一个)
boolean removeAll(Collection c):从集合中删除集合c里包含的所有元素(相当于用调用该方法的集合减集合c
boolean retainAll(Collection c):从集合中删除集合c里不包含的元素(相当于把调用该方法的集合变成该集合和集合c的交集
int size():该方法返回集合里元素的个数
Object[] toArray():该方法把集合转换成一个数组

8.2.1 使用Lambda表达式遍历集合

  • java 8 为Iterable接口新增了一个forEach(Consumer action) 默认方法(参数类型为函数式接口,因此可以使用Lambda表达式遍历集合元素)。
public class CollectionEach{
	public static void main(Stirng[] args){
		//创建一个集合
		Collection books = new HashSet();
		books.add("java讲义");
		books.add("c语言讲义");
		books.add("c++讲义");
		//调用forEach()方法遍历集合
		books.forEach(obj -> System.out.println("集合元素:" + obj));
	}
}

8.2.2 使用java 8 增强的Iterator遍历集合元素

  • Iterator主要用于遍历(迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器。
  • Iterator接口里定义了4个方法:

boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回true。
Object next():返回集合里的下一个元素。
void remove():删除集合里上一次next方法返回的元素。
void forEachRemaining(Consumer action):该方法可使用Lambda表达式来遍历集合元素。(Java8为Iterator新增的默认方法)

public class IteratorTest{
	public static void main(String[] args){
		//创建集合、添加元素的代码与前一个程序相同
		...
		//获取books集合对应的迭代器
		Iterator it = books.iterator();
		while(it.hasNext()){
			//it.next()方法返回的数据类型为Object类型,需要强制转换
			String books = (String)it.next();
			System.out.println(book);
			if(book.equlas("java讲义")){
				//从集合中删除上一次next()方法返回的元素
				it.remove();
			}
			//对book变量赋值,不会改变集合元素本身
			book = "测试字符串";
		}
		System.out.println(books);
	}
}
  • Iterator仅用于遍历集合,Iterator本身并不提供盛装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。
  • 当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何影响。
  • 迭代时Collection集合里的元素不能被改变,只有通过Iterator的 remove()方法删除上一次next()方法返回的集合元素才可以;否则将会引发java.util.Concurrent ModificationException异常。
  • Iterator迭代器采用的是快速失败(fail-fast)机制,一旦在迭代过程中检测到该集合已经被修改(通常是程序中的其他线程修改),程序立即引发异常。

8.2.3 使用Lambda表达式遍历Iterator

  • java 8 为Iterator新增了一个forEachRemaining(Consumer action) 方法(参数也是函数式接口)。程序会依次将集合元素传给Consumer的accept(T t) 方法。
Iterator it = books.iterator();
it.forEachRemaining(obj -> System.out.println("集合元素:" + obj));
  • Iterator的forEachRemaining()方法来遍历集合元素,参数是一个Lambda表达式(目标类型是Comsumer),因此上面代码也可用于遍历集合元素。

8.2.4 使用foreach循环遍历集合元素

for (Object obj : books){
	//此处的book变量也不是集合元素本身
	String book = (String) obj;
	System.out.println(book);
}
  • 使用foreach循环来迭代更加简洁。foreach循环中的迭代变量也不是集合元素本身,系统只是依次把集合元素的值赋给迭代变量,因此在foreach循环中修改迭代变量的值也没有任何实际意义
  • 同样,当使用foreach循环迭代访问集合元素时,该集合也不能被改变,否则将引发Concurrent ModificationException异常

8.2.5 使用java 8 新增的Predicate操作集合

java 8 为Collection集合新增了一个removeIf(Predicate filter)方法(批量删除符合filter条件的所有元素)。Predicate对象作为参数,也是函数式接口,可使用Lambda表达式。

//过滤条件:所有长度小于10的字符串元素都会被删除
books.removeIf(ele -> ((String)ele).length() < 10);

8.2.6 使用java 8 新增Stream操作集合

  • Java8还新增了Stream、IntStream、LongStream、DoubleStream等流式API,这些API代表多个支持串行和并行聚集操作的元素。上面4个接口中,Stream是一个通用的流接口,而IntStream、LongStream、DoubleStream则代表元素类型为int、long、double的流。
  • Java8还为上面每个流式API提供了对应的Builder,例如Stream.Builder、IntStream.Builder、LongStream.Builder、DoubleStream.Builder。
  • 独立使用Stream的步骤如下:
    ①使用StreamXxxStreambuilder()类方法创建该Stream对应的Builder。
    ②重复调用Builderadd()方法向该流中添加多个元素。
    ③调用Builderbuild()方法获取对应的Stream
    ④调用Stream的聚集方法
IntStream is = IntStream.builder()
	.add(20)
	.add(13)
	.add(-2)
	.add(18)
	.build();
	//下面调用聚集方法的代码每次只能执行一行
	System.out.println("is")
	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.print1n("is所有元素的平均值:”+is.average());
	System.out.print1n("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);
  • 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的map()方法就是中间方法。中间方法的返回值是另外一个流。
  • 末端方法:末端方法是对流的最终操作。当对某个Stream执行末端方法后,该流将会被“消耗”且不再可用。上面程序中的sum()、count()、average()等方法都是末端法。
  • 两个特征:
    有状态的方法:这种方法会给流增加一些新的属性,比如元素的唯一性、元素的最大数量、保证元素以排序的方式被处理等。有状态的方法往往需要更大的性能开销
    短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素。
  • Stream常用的中间方法:

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

  • Stream常用的末端方法:

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

  • 除此之外,Collection接口提供了一个stream()默认方法,该方法可返回该集合对应的流,接下来即可通过流式API来操作集合元素。
//统计书名包含“java”子串的图书数量
books.stratm().filter( ele -> ((String)ele).contins("java") ).count();
  • 程序只要调用Collectionstream()方法即可返回该集合对应的Stream,接下来就可通过Stream提供的方法对所有集合元素进行处理,这样大大地简化了集合编程的代码,这也是Stream编程带来的优势。

8.3 Set集合

Set集合由Set接口和Set接口的实现类组成,Set接口继承了Collection接口,因此包含了Collection接口的所有方法。(Set不允许包含重复元素)
后面介绍的HashSet、TreeSet、EnumSet三个实现类

8.3.1 HashSet类

  • HashSet是Set接口的典型实现,按Hash算法存储集合中的元素,存取查找性能。
  • HashSet具有以下特点:不能保证元素的排列顺序,顺序不同不是同步的;集合元素值可以是null
  • 向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。(如果两个元素equals()方法比较true,但hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。)
  • 把某个类的对象保存到HashSet集合中,重写这个类的equals()方法和hashCode()方法时,应该尽量保证两个对象通过equals()方法比较返回true时,它们的hashCode()方法返回值也相等
  • HashSet中如果有多个元素的hashCode值相同,但它们通过equals()方法比较返回false,就需要在一个“桶”里放多个元素(链式结构保存),这样会导致性能下降
HashSet hs = new HashSet();
  • 重写hashCode()方法的基本规则:

程序运行中,同一个对象多次调用hashCode()方法应该返回相同的值
两个对象通过equals()方法比较返回true时,两个的hashCode()方法应返回相等的值
对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值

  • 重写hashCode()方法的一般步骤:
    (1)计算出一个int类型的hashCode值;(2)用计算出来的对个hashCode值组合计算出一个hashCode值返回;
  • 当程序把可变对象添加到HashSet中之后,尽量不要去修改该集合元素中参与计算hashCode()、equals()实例变量,否则将会导致HashSet无法正确操作这些集合元素。

8.3.2 LinkedHashSet类

  • LinkedHashSet是HashSet的一个子类。使用链表维护元素的次序;遍历时按元素的添加顺序来访问集合里的元素;性能略低于HashSet(因为链表维护内部顺序)。
LinkedHashSet books = new LinkedHashSet();
  • LinkedHashSet依然是HashSet,依然不允许集合元素重复。

8.3.3 TreeSet类

  • TreeSet是SortedSet接口的实现类,可以确保集合元素处于排序状态
  • TreeSet提供几个额外的方法:

Comparator comparator():如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Comparator;如果TreeSet采用了自然排序,则返回null。
Object first():返回集合中的第一个元素
Object last():返回集合中的最后一个元素
Object lower(Objecte):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet集合里的元素)。
Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet集合里的元素)。
SortedSet subSet(Object fromElement,Object toElement):返回此Set的子集合范围从fromElement(包含)到toElement(不包含)。
SortedSet headSet(Object toElement):返回此Set的子集,由小于toElement的元素组成。
SortedSet tailSet(Object fromElement):返回此Set的子集,由大于或等于fromElement的元素组成。

  • TreeSet采用红黑树的数据结构来存储集合元素。

(1)自然排序

  • 自然排序:TreeSet回调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,按照升序排列。
  • 其他实现了Comparable接口的常用类:

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

Treeset ts = new TreeSet();
  • 如果把一个对象添加到TreeSet,则该对象的类必须实现Comparable接口,否则程序将会抛出异常。
  • 大部分类在实现compare To(Object obj)方法时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同类的两个实例才会比较大小。
  • 向TreeSet中添加的应该是同一个类的对象,否则也会引发ClassCastException异常。
  • 当把一个对象加入TreeSet集合中时,TreeSet 调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树结构找到它的存储位置。(如果两个对象通过compare To(Object obj)方法比较相等,新对象将无法添加到TreeSet集合中。)
  • 判断两个对象是否相等唯一标准是:两个对象通过compare To(Object obj)方法,返回0,TreeSet则会认为它们相等;否则就认为它们不相等。
  • 重写该对象对应类应保证有一致的结果,规则:如果两个对象通过equals()方法比较返回true时,这两个对象通过compare To(Object obj)方法比较应返回0
  • 向TreeSet中添加一个可变对象,并且后面程序修改了该可变对象的实例变量,将导致它与其他对象的大小顺序发生了改变,但TreeSet不会再次调整它们的顺序。(甚至可能导致保存的这两个对象通过compare To(Object obj)方法比较返回0。)
  • 推荐不要修改放入HashSet和TreeSet集合中元素的关键实例变量

(2)定制排序

  • 通过Comparator接口的帮助(接口里包含一个int compare(T o1, T o2)方法)。
  • 如果需要实现定制排序:则需要在创建TreeSet 集合对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。由于Comparator是一个函数式接口,因此可使用Lambda表达式来代替Comparator对象。
TreeSet ts = new TreeSet( (o1, o2) -> {
	M m1 = (M)o1;
	M m2 = (M)o2;
	//根据M对象的age属性决定大小:age越大,M对象反而越小
	return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0;
});
  • 依然不可以向TreeSet中添加类型不同的对象,否则引发异常。
  • TreeSet判断两个集合元素相等的标准是:通过Comparator(或Lambda表达式)比较两个元素返回了0,这样TreeSet不会把第二个元素添加到集合中。

8.3.4 EnumSet类

  • EnumSet是一个专为枚举类设计的集合类,所有元素都必须是指定枚举类型的枚举值(创建时显式或隐式的指定),也是有序的。
  • 内部以位向量的形式存储,占用内存很小,运行效率很好,尤其批量操作。
  • EnumSet集合不允许加入null元素,将会抛出异常。
EnumSet es = EnumSet.allOf(Season.class);
  • EnumSet没有暴露任何构造器,应该通过它提供的类方法来创建EnumSet对象:

EnumSet allOf(Class elementType):包含指定枚举类所有枚举值的EnumSet集合。
EnumSet complementOf(EnumSet s):其元素类型指定EnumSet里元素类型相同的EnumSet集合,新EnumSet集合包含原EnumSet集合所不包含的、此枚举类剩下的枚举值(即新EnumSet集合和原EnumSet集合的集合元素加起来就是该枚举类的所有枚举值)。
EnumSet copyOf(Collection c):使用一个普通集合来创建EnumSet集合。
EnumSet copyOf(EnumSet s):与指定EnumSet具有相同元素类型、相同集合元素的EnumSet集合。
EnumSet noneOf(Class elementType):元素类型为指定枚举类型EnumSet。
EnumSet of(E first,E...rest):包含一个多个枚举值的EnumSet集合,传入的多个枚举值必须属于同一个枚举类
EnumSet range(E from,E to):创建一个包含从from枚举值到to枚举值范围内所有枚举值的EnumSet集合。

  • 还可以复制另外一个EnumSet集合或者Collection集合中的所有元素来创建新的EnumSet集合。(赋值Collection集合时,要求Collection集合的所有元素必须是同一个枚举类的枚举值)

8.3.5 各Set实现类的性能分析

  • HashSet和TreeSet是Set的两个典型实现,HashSet性能总是比TreeSet好(TreeSet红黑树算法额外维护)。
  • 只有需要保持排序的Set时,才应该使用TreeSet
  • HashSet有一个子类:LinkedHashSet插入删除比HashSet略慢,由于链表额外维护;但以为链表,遍历会更快。
  • EnumSet是所有Set实现类中性能最好的,但只能保存同一个枚举类的枚举值作为集合元素。
  • Set的三个实现类HashSet、TreeSet、EnumSet都是线程不安全的。(若多个线程访问,需通过Collections工具类的synchronizeedSortedSet方法来“包装”该Set集合,最好在创建时进行)

8.4 List集合

List集合代表一个元素有序可重复的集合,都有其对应的顺序索引。默认按元素的添加顺序设置元素的索引。

8.4.1 java 8 改进的List接口和ListIterator接口

  • List作为Collection接口的子接口,可以使用Collection接口里的方法。
List books = new ArrayList();
  • List是有序集合,所以增加了一些根据索引来操作集合元素的方法:

void add(int index,Object element):将元素 element 插入到List集合的index处
boolean addAll(int index,Collection c):将集合c所包含的所有元素都插入到List集合的index处
Object get(int index):返回集合index索引处的元素。
int indexOf(Object o):返回对象o在List集合中第一次出现的位置索引。
int lastIndexOf(Object o):返回对象o在List集合中最后一次出现的位置索引。
Object remove(int index)删除并返回index索引处的元素。
Object set(int index,Object element):将index索引处的元素替换element对象,返回被替换的旧元素。
List subList(int fromlndex,int tolndex):返回从索引fromIndex(包含)到索引tolndex(不包含)

  • 所有的List实现类都可以调用这些方法来操作集合元素。
  • List增加了根据索引来插入替换删除集合元素的方法。
    新增的两个默认方法:

void replaceAll(UnaryOperator operator):根据operator指定的计算规则重新设置List集合的所有元素。
void sort(Comparator c):根据Comparator参数对List集合的元素排序

  • List判断两个对象相等:通过equals()方法比较返回true即可。
  • 当调用List的set(int index,Object element)方法来改变List集合指定索引处的元素时,指定的索引必须是List集合的有效索引。即set(int index,Object element)方法不会改变List集合的长度
  • 与Set只提供了一个iterator()方法不同,List还额外提供了一个listIerator()方法,该方法返回一个Listlterator对象,Listlterator接口继承了Iterator接口,提供了专门操作List的方法。
  • Listlterator接口在Iterator 接口基础上增加了如下方法:

boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素
Object previous():返回该迭代器的上一个元素
void add(Object o):在指定位置插入一个元素

8.4.2 ArrayList和Vector实现类

  • ArrayList和Vector作为List类的两个典型实现,完全支持前面的List接口的全部功能。
  • ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态的、允许再分配的Object[ ]数组。ArrayList或Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,它们的initialCapacity会自动增加
  • 添加大量元素时,可使用ensureCapacity(int minCapaticy)方法一次性增加。
  • 可以在创建时就指定initialCapacity的大小(若空或不指定,数组默认长度为10)
  • ArrayList和Vector还提供了如下两个方法来重新分配Object[ ]数组

void ensureCapacity(int minCapacity):将ArrayList 或Vector集合的Object[]数组长度增加大于或等于minCapacity值。
void trimToSize():调整ArrayList 或Vector集合的Object[]数组长度为当前元素的个数。可减少占用的存储空间。

  • ArrayList和Vector在用法上几乎完全一样,但Vector是个古老的集合,有很多缺点,尽量少用。
  • ArrayList是线程不安全的,Vector线程安全(也不推荐用)。
  • Vector还提供一个stack子类,用于模拟“栈”,“后进先出(LIFO)”。

Object peek():返回“栈”的第一个元素,但并不将该元素“pop”出栈
Object pop():返回“栈”的第一个元素,并将该元素“pop”出栈
void push(Object item):将一个元素“push”进栈,最后一个进“栈”的元素总是位于“栈”顶。

也是比较古老,尽量少用,推荐使用ArrayDeque后面讲。

8.4.3 固定长度的List

  • Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加删除该集合里的元素。

8.5 Queue集合

Queue用于模拟队列这种数据结构,“先进先出FIFO”。Queue接口中定义了几个方法:

void add(Objecte):将指定元素加入此队列的尾部
Object (element):获取队列头部的元素,但是不删除该元素。
boolean offer(Objecte):将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比add(Objecte)方法更好。
Object peek():获取队列头部的元素,但是不删除该元素。如果此队列为空,则返回null。
Object poll():获取队列头部的元素,并删除该元素。如果此队列为空,则返回null。
Object remove():获取队列头部的元素,并删除该元素。

8.5.1 PriorityQueue实习类

  • PriorityQueue是一个比较标准的队列实现类。(保存队列元素的顺序并不是加入队列的顺序,而是按队列元素的大小进行重新排序)
  • PriorityQueue已经违反了队列的最基本规则:先进先出(FIFO)。
PriorityQueue pq = new PriorityQueue();
  • PriorityQueue不允许插入null元素,还要对队列进行排序(两种排序方式,自然排序、定制排序,同TreeSet参考8.3.3节)。

8.5.2 Deque接口与ArrayDeque实现类

  • Deque接口是Queue接口的子接口,代表一个双端队列
  • Deque接口的一些方法:

void addFirst(Objecte):将指定元素插入该双端队列的开头
void addLast(Objecte):将指定元素插入该双端队列的末尾
Iterator descendinglterator():返回该双端队列对应的迭代器,该迭代器将以逆向顺序来迭代队列中的元素。
Object getFirst()获取不删除双端队列的第一个元素
Object getLast()获取不删除双端队列的最后一个元素
boolean offerFirst(Object e):将指定元素插入该双端队列的开头
boolean offerLast(Objecte):将指定元素插入该双端队列的末尾
Object peekFirst()获取不删除该双端队列的第一个元素;如果此双端队列为空,则返回null。
Object peekLast()获取不删除该双端队列的最后一个元素;如果此双端队列为空,则返回null。
Object pollFirst()获取删除该双端队列的第一个元素;如果此双端队列为空,则返回null。
Object pollLast()获取删除该双端队列的最后一个元素;如果此双端队列为空,则返回null。
Object pop()(**栈方法**)pop出该双端队列所表示的栈的栈顶元素。相当于removeFirst()。
void push(Object e)(**栈方法**):将一个元素push进该双端队列所表示的栈的栈顶。相当于addFirst(e)。
Object removeFirst()获取删除该双端队列的第一个元素
Object removeFirstOccurrence(Object o)删除该双端队列的第一次出现的元素o
Object removeLast()获取删除该双端队列的最后一个元素
boolean removeLastOccurrence(Object o)删除该双端队列的最后一次出现的元素o

  • Deque 不仅可以当成双端队列,也可以当成
ArrayDeque stack = new ArrayDeque();//当成“栈”
ArrayDeque queue = new ArrayDeque();//当成“队列”
  • ArrayDeque:Deque接口提供的一个典型的实现类。(基于数组实现的双端队列,可以指定Object[]数组的长度,若不指定长度为16)。
  • ArrayList和ArrayDeque两个集合类的实现机制基本相似(底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,超出系统重新分配)。
  • ArrayDeque当成“栈”(推荐使用ArrayDeque当栈,避免使用Stack古老)
    在这里插入图片描述
public class ArrayDequeStack{
	public static void main(String[] args){
		ArrayDeque stack = new ArrayDeque();
		//依次将三个元素push“入栈”
		stack.push("疯狂Java讲义");
		stack.push("轻量级Java EE企业应用实战");
		stack.push("疯狂Android讲义");
		//输出:[疯狂Android讲义,轻量级Java EE企业应用实战,疯狂Java讲义]
		System.out.println(stack);
		//访问第一个元素,但并不将其pop出“栈”,输出:疯狂Android讲义
		System.out.println(stack.peek());
		//依然输出:[疯狂Android 讲义,疯狂Java讲义,轻量级Java EE企业应用实战]
		System.out.println(stack);
		//pop出第一个元素,输出:疯狂Android 讲义
		System.out.println(stack.pop());
		//输出:[轻量级Java EE企业应用实战,疯狂Java讲义]
		System.out.println(stack);
	}
}
  • ArrayDeque当成“队列”
    在这里插入图片描述
public class ArrayDequeQueue{
	public static void main(String[] args){
		ArrayDeque queue = new ArrayDeque();
		//依次将三个元素“入队”
		queue.offer("疯狂Java讲义");
		queue.offer("轻量级Java EE企业应用实战");
		queue.offer("疯狂Android讲义");
		//输出:[疯狂Java讲义,轻量级JavaEE企业应用实战,疯狂Android讲义]
		System.out.println(queue);
		//访问队列头部的元素,但并不将其po11出队列“栈”,输出:疯狂Java讲义
		System.out.println(queue.peek());
		//依然输出:[疯狂Java 讲义,轻量级Java EE企业应用实战,疯狂Android讲义]
		System.out.println(queue);
		//po11出第一个元素,输出:疯狂Java讲义
		System.out.println(queue.poll());
		//输出:[轻量级Java EE企业应用实战,疯狂Android讲义]
		System.out.print1n(queue);
	}
}

8.5.3 LinkedList实现类

  • LinkedList类是List接口的实现类,意味着他是一个List集合,可以根据索引来随机访问集合中的元素。
  • LinkedList还实现了Deque接口,可以被当成双端队列来使用(可以当“栈”、“队列”)
  • 下面示范了LinkedList作为List集合双端队列的用法:
public class LinkedListTest{
	public static void main(String[] args){
		LinkedList books = new LinkedList();
		//将字符串加入队列的尾部
		books.offer("疯狂java讲义");
		//将一个字符串元素加入栈的顶部
		books.push("轻量级Java EE企业应用实战");
		//将字符串元素添加到队列的头部(相当于栈顶)
		books.offerFirst("疯狂Android讲义");
		//以List的方式(按索引)遍历集合元素
		for(int i = 0; i < books.size(); i++){
			System.out.println(books.get(i));
		}
		//访问不删除栈顶元素
		System.out.println(books.peekFirst());
		//访问不删除队列的最后一个元素
		System.out.println(books.peekLast());
		//栈顶元素弹出栈
		System.out.println(books.pop());
		//下面输出看到队列中第一个元素被删除
		System.out.println(books);
		//访问删除队列最后一个元素
		System.out.println(books.pollLast());
		//下面输出:"轻量级Java EE企业应用实战"
		System.out.println(books);
	}
}
  • ArrayListArrayDeque内部以数组的形式保存集合中的元素(随机访问性能好,比使用Iterator迭代器性能都好)
  • LinkedList内部以链表的形式保存集合中的元素(插入删除性能好)

8.5.4 各种线性表的性能分析

  • Java 提供的List 就是一个线性表接口,而ArrayList(基于数组的线性表)、LinkedList(基于链表的线性表)又是线性表的两种典型实现。Queue代表了队列Deque代表了双端队列(既可作为队列使用,也可作为使用)
  • 总体来说,ArrayList比LinkedList的性能要好,大部分时候建议使用ArrayList
  • 使用List集合建议:(1)遍历时,ArrayList、Vector集合,使用随机访问方法 get 遍历;LinkedList集合,使用迭代器(Iterator)遍历。(2)经常执行插入、删除操作来改变大量数据的List集合,建议使用LinkedList集合。(3)多个线程需要同时访问List集合中的元素,考虑使用Collections将集合包装成线程安全的集合。

8.6 java 8 增强的Map集合

  • Map具有映射关系的数据,保存两组值key 和value (key和value都可以是任何引用类型数据),key不允许重复。
  • key和value之间存在单向一对一关系。
  • Map里包含了一个keySet()方法,返回Map里所有key组成的Set集合。
  • Map提供了一个Entry内部类来封装key-value对,而计算Entry存储时则只考虑Entry封装的key。
  • 从Java源码来看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合
  • 如果把Map里所有的key放在一起看,它们就组成了一个Set集合(key没有顺序,不能重复)
  • 如果把Map里的所有value放在一起看,它们又非常类似于一个List(元素之间可以重复,根据索引来查找),只是Map中的索引不再是整数值,而是另一个对象。
  • 如果需要从Map中取出元素,需要该元素的key索引,因此,Map有时也被称为字典,或关联数组。
  • Map接口中定义的常用方法:

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

  • Map中包括一个内部类Entry,封装了一个key-value对。Entry包含如下三个方法:

Object getKey():返回该Entry里包含的key值
Object getValue():返回该Entry里包含的value值
Object setValue(V value):设置该Entry里包含的value值,并返回新设置的value值。

8.6.1 java 8 为Map新增的方法

  • Java8除为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 computelfAbsent(Object key,Function mappingFunction):如果传给该方法的key参数在Map中对应的value为null,则使用mappingFunction根据key计算一个新的结果,如果计算结果不为null,则用计算结果覆盖原有的value。如果原Map原来不包括该key,那么该方法可能会添加一组key-value对。
Object computelfPresent(Object key,BiFunction remappingFunction):如果传给该方法的key参数在Map中对应的value不为null,该方法将使用remappingFunction根据原key、value计算一个新的结果,如果计算结果不为null,则使用该结果覆盖原来的 value;如果计算结果为null,则删除原key-value对。
void forEach(BiConsumer action):该方法是Java8为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。与传统putO方法不同的是,该方法不可能添加新的key-value对。如果尝试替换的key在原Map中不存在,该方法不会添加key-value对,而是返回null。
boolean replace(K key,V old Value,V newValue):将Map 中指定key-value对的原value替换成新value。如果在Map中找到指定的key-value对,则执行替换并返回true,否则返回false。
replaceAll(BiFunction function):该方法使用BiFunction对原key-value对执行计算,并将计算结果作为该key-value对的value值。

8.6.2 java 8 改进的HashMap和Hashtable实现类

  • HashMap 和Hashtable都是Map接口的典型实现类。Hashtable是一个古老的Map实现类(不推荐使用)。
Hashtable ht = new Hashtable();
HashMap ht = new HashMap();
  • Hashtable和HashMap有两点区别:(1)Hashtable是线程安全的,HashMap不是(但性能高一点);Hashtable不允许使用null作为key和value,将引发异常,HashMap可以。
  • 尽量少用Hashtable实现类,即使需要创建线程安全的Map实现类,可以通过后面介绍的Collections工具类把HashMap变成线程安全的。
  • 为了成功地在HashMap、Hashtable中存储、获取对象,用作key的对象必须实现hashCode()方法和equals()方法。
  • HashMap、Hashtable不能保证其中key-value对的顺序。
    (1)判断两个key相等的标准也是:两个key 通过equals()方法比较返回true,两个key的hashCode值也相等;
    (2)判断两个value相等的标准更简单:只要两个对象通过equals)方法比较返回true即可。
  • HashMap、Hashtable中还包含一个containsValue()方法,用于判断是否包含指定的value。
  • 使用自定义类作为key时,如果重写该类的equals(Object obj)和hashCode()方法,则应该保证两个方法的判断标准一致——当两个key 通过equals0方法比较返回true时,两个key的hashCode()返回值也应该相同。
  • 使用可变对象作为key,并且修改了作为key的可变对象,则可能出现:程序无法准确访问到Map中被修改过的key。(尽量不要使用可变对象,修改作为key的可变对象)

8.6.3 LinkedHashMap实现类

  • HashSet 有一个LinkedHashSet子类,HashMap也有一个LinkedHashMap子类;
LinkedHashMap lhm = new LinkedHashMap();
  • LinkedHashMap也使用双向链表来维护key-value对的次序(其实只需要考虑key的次序),迭代顺序与key-value对的插入顺序保持一致
  • LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能;但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时将有较好的性能

8.6.4 使用Properties读写属性文件

  • Properties类是Hashtable类的子类,可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的“属性名=属性值”
    加载到Map对象中。Properties里的key、value都是字符串类型
  • 提供三个方法来修改Properties里的key、value值:

String getProperty(String key):获取Properties中指定属性名对应的属性值,类似于Map的get(Object key)方法。
String getProperty(String key,String defaultValue):该方法与前一个方法基本相似。该方法多一个功能,如果Properties中不存在指定的key时,则该方法指定默认值。
Object setProperty(String key,String value)设置属性值,类似于Hashtable的put)方法。

  • 还提供了两个读写属性文件的方法:

void load(InputStream inStream):从属性文件(以输入流表示)中加载key-value对,把加载到的key-value 对追加到Properties里(Properties是Hashtable的子类,它不保证key-value对之间的次序)。
void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的属性文件(以输出流表示)中。

  • Properties可以把key-value对以XML文件的形式保存起来,也可以从XML文件中加载key-value对,用法与此类似,此处不再赘述。

8.6.5 SortedMap接口和TreeMap实现类

  • 正如 Set 接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类。
  • 同样 Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类。
TreeMap tm = new TreeMap();
  • TreeMap就是一个红黑树数据结构,每个key-value对作为红黑树的一个节点。根据key对节点进行排序,两种排序方式:自然排序、定制排序。
  • 类似于TreeSet,TreeMap中判断两个key相等的标准是:两个key通过compareTo()方法返回0。
  • 使用自定义类作为TreeMap的key,则重写该类的equals()方法和compareTo()方法时应保持一致的返回结果:两个key通过equals()方法比较返回true时,它们通过compareTo()方法比较应该返回0
  • 再次强调:SetMap的关系十分密切,Java源码就是先实现了HashMap、TreeMap等集合,然后通过包装一个所有的value都为null的Map集合实现了Set集合类。
  • 与TreeSet类似,TreeMap中提供一系列根据key顺序访问key-value的方法:

Map.Entry firstEntry():返回该Map中最小key所对应的key-value对,如果该Map为空,则返回null。
Object firstKe():返回该Map中的最小key值,如果该Map为空,则返回null。
Map.Entry lastEntry():返回该Map中最大key所对应的key-value对,如果该Map为空或不存在这样的key-value对,则都返回null。
Object lastKey():返回该Map中的最大key值,如果该Map为空或不存在这样的key,则都返回null。
Map.Entry higherEntry(Object key):返回该Map中位于key后一位的key-value对(即大于指定key的最小key 所对应的key-value对)。如果该Map为空,则返回null。
Object higherKey(Object key):返回该Map中位于key后一位的key值(即大于指定key的最小key值)。如果该Map为空或不存在这样的key-value对,则都返回null。
Map.Entry lowerEntry(Object key):返回该Map中位于key前一位的key-value对(即小于指定key的最大key所对应的key-value对)。如果该Map为空或不存在这样的key-value对,则都返回null。
Object lowerKey(Object key):返回该Map中位于key前一位的key值(即小于指定key的最大key值)。如果该Map为空或不存在这样的key,则都返回null。
NavigableMap subMap(Object fromKey,boolean fromlnclusive,Object toKey,boolean tolnclusive):返回该Map的子Map,其key的范围是从fromKey(是否包括取决于第二个参数)到toKey(是否包括取决于第四个参数)。
SortedMap subMap(Object fromKey,Object tokey):返回该Map的子Map,其key的范围是从fromKey(包括)到toKey(不包括)
SortedMap tailMap(Object fromKey):返回该Map的子Map,其key的范围是大于fromKey(包括)的所有key
NavigableMap tailMap(Object fromKey,boolean inclusive):返回该Map的子Map,其key的范围是大于fromKey(是否包括取决于第二个参数的所有key
SortedMap headMap(Object toKey):返回该Map的子Map,其key的范围是小于toKey(不包括)的所有key
NavigableMap headMap(Object toKey,boolean inclusive):返回该Map的子Map,其key的范围是小于toKey(是否包括取决于第二个参数的所有key

  • 其实它们很简单。因为TreeMap中的key-value对是有序的,所以增加了访问第一个、前一个、后一个、最后一个key-value对的方法,并提供了几个从TreeMap中截取子TreeMap的方法。

8.6.6 WeakHashMap实现类

  • WeakHashMapHashMap的用法基本相似。区别在于,HashMap的key保留了对实际对象的强引用,不会被垃圾回收,也不会自动删除这些key所对应的key-value对;但WeakHashMap的key只保留了对实际对象的弱引用,这些key所引用的对象可能被垃圾回收,也可能自动删除这些key所对应的key-value对。
WeakHashMap whm = new WeakHashMap();

8.6.7 IdentityHashMap实现类

  • 这个Map实现类的实现机制与HashMap基本相似,但它在处理两个key相等时比较独特:在IdentityHashMap中,当且仅当两个key 严格相等(key1==key2)时,IdentityHashMap 才认为两个key相等;对于普通的HashMap而言,只要keyl和key2通过equals()方法比较返回true,且它们的hashCode值相等即可。
IdentityHashMap ihm = new IdentityHashMap();
  • IdentityHashMap是一个特殊的Map实现!它有意违反Map的通常规范:IdentityHashMap要求两个key严格相等时才认为两个key相等。

8.6.8 EnumMap实现类

  • EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式隐式指定它对应的枚举类
EnumMap enumMap = new EnumMap(Season.class);
  • EnumMap具有如下特征
    (1)EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑高效
    (2)EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)来维护key-value对的顺序。当程序通过keySet()、entrySet()、values()等方法遍历EnumMap时可以看到这种顺序。
    (3)EnumMap不允许使用null作为key,但允许使用null作为value。(如果试图使用null作为key时将抛出NullPointerException异常。如果只是查询是否包含值为null的key,或只是删除值为null的key,都不会抛出异常。)
  • 与创建普通的Map有所区别的是,创建EnumMap时必须指定一个枚举类,从而将该EnumMap和指定枚举类关联起来

8.6.9 个Map实现类的性能分析

  • 对于Map的常用实现类而言,虽然HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,因此HashMap通常比Hashtable要快。
  • TreeMap 通常比HashMap、Hashtable要慢(尤其在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理key-value对(红黑树的每个节点就是一个key-value对)。
  • 使用TreeMap有一个好处:TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。当TreeMap被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray)方法生成key的数组,接下来使用Arrays的binarySearch0方法在已排序的数组中快速地查询对象。
  • 对于一般的应用场景,程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的(HashMap底层其实也是采用数组来存储key-value对)。但如果程序需要一个总是排好序的Map时,则可以考虑使用TreeMap
  • LinkedHashMap 比HashMap慢一点,因为它需要维护链表来保持Map中key-value时的添加顺序。
  • IdentityHashMap性能没有特别出色之处,因为它采用与HashMap基本相似的实现,只是它使用==而不是equals0方法来判断元素相等。
  • EnumMap的性能最好,但它只能使用同一个枚举类的枚举值作为key。

8.7HashSet和HashMap的性能选项

  • 对于HashSet及其子类而言,它们采用hash算法来决定集合中元素的存储位置,并通过hash算法来控制集合的大小
  • 对于HashMapHashtable及其子类而言,它们采用hash 算法来决定Map中key的存储,并通过hash算法来增加key集合的大小
  • 可能发生“hash冲突”,冲突元素以链表形式存储。
    在这里插入图片描述
  • HashSet、HashMap的hash表包含如下属性:

容量(capacity):hash表中的桶数量。
初始化容量(initial capacity):创建hash表时桶的数量。HashMap和HashSet都允许在构造器中指定初始化容量。
尺寸(size):当前hash表中记录的数量。
负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的hash表,依此类推。轻负载的hash表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)。

  • 除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing
  • HashSet和HashMap、Hashtable的构造器允许指定一个负载极限,默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。
  • 使用足够大的初始化容量创建 HashSet和HashMap、Hashtable时,可以更高效地增加记录,但将初始化容量设置太高可能会浪费空间,因此通常不要将初始化容量设置得过高。

8.8 操作集合的工具类:Collections

8.8.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,inti,intj):将指定List集合中的i处元素和j处元素进行交换
void rotate(List list,int distance):当distance为正数时,将list集合的后distance个元素“整体”移到前面;当distance为负数时,将list集合的前distance个元素“整体”移到后面。该方法不会改变集合的长度。

8.8.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,Objecto):返回指定集合中指定元素的出现次数
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。

8.8.3 同步控制

  • Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合 包装成线程同步 的集合,从而可以解决多线程并发访问集合时的线程安全问题。
  • Java中常用的集合框架中的实现类HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap和TreeMap都是线程不安全的
public class SynchronizedTest{
	public static void main(String[] args){
		//下面程序创建了4个线程安全的集合对象
		Collection c = Collections.synchronizedCollection(new ArrayList());
		List list = Collections.synchronizedList(new ArrayList());
		Set s = Collections.synchronizedSet(new HashSet());
		Map m = Collections.synchronizedMap(new HashMap());
	}
}

8.8.4 设置不可变集合

  • 不可变的集合对象:只能访问集合元素,不可修改集合元素。
  • Collections提供了三类方法来返回一个不可变的集合:(三类方法的参数是原有的集合对象,返回值是该集合的“只读”版本。)

emptyXxx():返回一个空的、不可变的集合对象,此处的集合既可以是List,也可以是SortedSet、Set,还可以是Map、SortedMap等。
singletonXxx():返回一个只包含指定对象(只有一个或一项元素)的、不可变的集合对象,此处的集合既可以是List,还可以是Map。
unmodifiableXxx():返回指定集合对象的不可变视图,此处的集合既可以是List,也可以是Set、SortedSet,还可以是Map、SorteMap等。

//创建一个空的、不可改变的List对象
List unmodifiableList = Collections.emptyList();
//创建一个只有一个元素、不可改变的Set对象
Set unmodifiableSet = Collections.singleton("java");
//返回一个普通的Map对象对应的比可变版本
Map unmodifiableMap = Collectins.unmodifialeMap(scores);

8.8.5 java 9 新增的不可变集合

  • 程序直接调用Set、List、Map的of()方法即可创建包含N个元素的不可变集合,这样一行代码就可创建包含N个元素的集合。
  • 不可变意味着程序不能向集合中添加元素,也不能从集合中删除元素。
//创建包含4个元素的Set集合
Set set = Set.of("java","kotlin","go","swift");
//创建包含4个元素的List集合
List list = List.of(34, -25, 67, 231);
//创建包含3个key-value对的Map集合
Map map = Map.of("语文", 89, "数学", 82, "英语", 92);
//使用Map.entry()方法显示构建key-value对
Map map2 = Map.ofEntries(Map.entry("语文", 89),
		Map.entry("数学", 82),
		Map.entry("英语", 92));

8.9 烦琐的接口:Enumeration

  • Enumeration接口是Iterator迭代器的“古老版本”,有两个方法名字很长,不推荐使用。
  • 应该尽量采用Iterator迭代器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值