JDK17 |java17学习 输出和文件
Technical requirements
为了能够执行本章提供的代码示例,您将需要以下内容:
- 装有 Microsoft Windows、Apple macOS 或 Linux 操作系统的计算机
- Java标准版 (SE) 17 或更高版本
- 集成开发环境 (IDE) 或您首选的代码编辑器
本书的第 1 章,Java 17 入门。本章的代码示例文件可在 GitHub 上的 https://github .com/PacktPublishing/Learn-Java-17-Programming.git 存储库,位于 examples/src/main/java/com/packt/learnjava/ch06_collections 文件夹中.
List, Set, and Map interfaces
Java 集合框架由类和 实现 集合数据结构的接口。集合类似于数组,因为它们可以保存对对象的引用并且可以作为一个组进行管理。不同之处在于,数组需要在使用之前定义其容量,而集合可以根据需要自动增加和减少其大小。您只需添加或删除对集合的对象引用,集合就会相应地更改其大小。另一个区别是集合的元素不能是原始类型,例如 short、int 或 双。如果需要存储此类类型值,则元素必须是对应的包装类型,例如 Short、Integer 或双。
Java 集合支持存储和访问集合元素的各种算法:有序列表、唯一集、字典(称为 map
in Java),一个 stack,一个 queue 和一些 其他。 Java集合框架的所有类和接口都属于的java.util包 Java 类库 (JCL)。 java.util 包包含以下内容:
- 扩展
Collection接口的接口:List、Set和Queue,列出最流行的 - 实现前面列出的接口的类:
ArrayList、HashSet、Stack、LinkedList等 Map接口及其ConcurrentMap和SortedMap子接口,以命名一对夫妇- 实现
Map相关接口的类:HashMap、HashTable和< code class="literal">TreeMap,列举最常用的三个
查看 java.util 包的所有类和接口需要一本专门的书。因此,在本节中,我们将简要概述三个主要接口 - List、Set 和 class="literal">Map——以及它们每个的一个实现类——ArrayList、HashSet和HashMap。我们从 List 和 Set 接口共享的方法开始。 List 和 Set 的主要区别在于 Set 不允许元素的重复。另一个区别是 List 保留了元素的顺序并允许对它们进行排序。
为了识别集合中的元素,使用 equals() 方法。为了提高性能,实现 Set 接口的类也经常使用 hashCode() 方法。这有助于在大多数情况下(但并非总是如此)快速计算整数(称为 哈希值 或 哈希码) ) 对每个元素都是唯一的。元素 与 相同的哈希值被放置在同一个 bucket 中。在确定集合中是否已经存在某个值的同时,检查内部哈希表并查看该值是否已被使用就足够了。如果不是,则新元素是唯一的。如果是,则可以将新元素与具有相同哈希值的每个元素进行比较(使用 equals() 方法)。这样的过程比将新元素与集合中的每个元素逐个比较要快。
这就是为什么我们经常看到一个类的名字有一个hash前缀,说明这个类使用了一个hash值,所以元素必须实现hashCode() 方法。执行此操作时,您必须确保已实现它,以便每次 equals() 方法返回 true for两个对象,
hashCode() 方法也一样。否则,刚才描述的使用哈希值的算法将不起作用。
最后,在谈论 java.util 接口之前,先谈谈泛型。
Generics
在前面的示例中,generics 是用尖括号括起来的元素性质声明。如您所见,它们是多余的,因为它们在赋值语句的左侧和右侧重复出现。这就是为什么 Java 允许用空括号 (<>) 替换右侧的泛型,称为 a 钻石,如以下代码片段所示:
泛型通知编译器有关集合元素的预期类型。这样,编译器可以检查程序员试图添加到已声明集合中的元素是否属于兼容类型。例如,请注意以下事项:
这有助于避免运行时错误。它还提示程序员(因为 IDE 会在程序员编写代码时编译代码)关于可能对集合元素进行的操作。
我们还将看到这些其他类型的泛型:
表示 一个类型是T或者是T,其中T是用作集合泛型的类型。表示一种类型T或其任何基类(父) 类,其中T是用作集合泛型的类型。
有了这个,让我们从如何创建实现 List 或 Set 接口的类的对象开始——或者,在也就是说,List或Set类型的变量可以被初始化。为了演示这两个接口的方法,我们将使用两个类:ArrayList(实现List)和HashSet(实现 Set)。
How to initialize List and Set
由于 Java 9,List 或 Set 接口 具有可用于初始化集合的静态 of() 工厂方法,如下所述:
of():返回一个空集合。of(E... e):返回一个集合,其中包含调用期间传入的元素数量。它们可以以逗号分隔的列表或数组的形式传递。
这里有一些例子:
如您所料,Set 的工厂方法不允许重复,因此我们将这一行注释掉(否则,前面的示例将在该行停止运行)。不太期望的是,您不能拥有 null 元素,并且在使用 of() 方法。这就是为什么我们注释掉了前面例子的一些行。如果您需要在集合初始化后添加元素,您必须使用构造函数或其他一些创建可修改集合的实用程序对其进行初始化(我们将看到 Arrays.asList()< /code> 很快)。
Collection 接口提供了两种向实现了 Collection 的对象添加元素的方法(List 和 Set) 如下所示:
boolean add(E e):这会尝试将提供的元素e添加到集合中;它在成功的情况下返回true,在无法完成的情况下返回false(例如,当这样一个Set接口中已经存在元素)。boolean addAll(Collection c):这会尝试将提供的集合中的所有元素添加到集合中;如果添加了至少一个元素,则返回true,如果无法添加 false _idIndexMarker738">一个 元素到集合(例如,当提供的集合c的所有元素已经存在时Set界面中)。
下面是一个使用 add() 方法的例子:
下面是一个使用 addAll() 方法的例子:
请注意,在 前面代码片段中的最后一个示例中,set1.addAll( set2) 方法返回 true,尽管并未添加所有元素。查看 add() 和 addAll() 方法返回 false
,看下面的例子:
ArrayList 和 HashSet 类也有接受集合的构造函数,如以下代码片段所示:
现在,在 我们 了解了如何初始化集合之后,我们可以转向 List 和 Set 接口。
java.lang.Iterable interface
Collection 接口 扩展了 java.lang.Iterable 接口,这意味着那些实现 Collection 接口的类——无论是否直接——也实现了 java.lang.Iterable 接口。 Iterable 接口中只有三个方法,如下所述:
迭代器<T> iterator():返回一个实现java.util.Iterator接口的类的对象;它允许在FOR语句中使用该集合,如下例所示:default void forEach (Consumer function):这应用了函数">Consumer类型到集合的每个元素,直到所有元素都被处理或函数抛出异常。我们将在第 13 章中讨论什么是函数>, 函数式编程;现在,我们将在这里仅提供一个示例:默认拆分器<T> splititerator():返回一个实现java.util.Spliterator接口的类的对象;它主要用于实现允许并行处理的方法,超出了本书的范围。
Collection interface
正如我们已经 提到的,List 和 Set 接口扩展了 < code class="literal">Collection接口,表示Collection接口的所有方法都被List 和 设置。这些方法在这里列出:
boolean add(E e):这会尝试将元素添加到集合中。boolean addAll(Collection c):这会尝试添加所提供集合中的所有元素。boolean equals(Object o):将集合与提供的o对象进行比较。如果提供的对象不是集合,则此对象返回false;否则,它将集合的组成与提供的集合的组成进行比较(作为o对象)。在List的情况下,它还比较元素的顺序。让我们用几个例子来说明这一点,如下所示:int hashCode():这个返回集合的哈希值;它用于集合是需要hashCode()方法实现的集合的元素的情况。boolean isEmpty():如果集合没有任何元素,则返回true。int size():返回集合的元素个数;当isEmpty()方法返回true时,该方法返回0。void clear():这会从集合中删除所有元素;调用该方法后,isEmpty()方法返回true,size( )方法返回0。boolean contains(Object o):如果集合包含提供的o<,则返回true/代码>对象。要使此方法正常工作,集合的每个元素和提供的对象都必须实现equals()方法,并且对于设置,应该实现hashCode()方法。boolean containsAll(Collection :如果集合包含提供的集合中的所有元素,则返回true。要使此方法正常工作,集合的每个元素和提供的集合的每个元素都必须实现equals()方法,并且对于设置,应该实现hashCode()方法。boolean remove(Object o):这会尝试从该集合中删除指定的元素,如果存在则返回true。要使此方法正常工作,集合的每个元素和提供的对象都必须实现equals()方法,并且对于设置,应该实现hashCode()方法。boolean removeAll(Collection :这会尝试从集合中移除所提供集合的所有元素;类似于addAll()方法,如果至少有一个元素被删除,此方法返回true;否则,它返回false。要使此方法正常工作,集合的每个 元素和提供的集合的每个元素都必须实现equals()方法,并且在Set的情况下,应该实现hashCode()方法。default boolean removeIf(Predicate filter):这会尝试从集合中移除所有满足给定谓词的元素;这是我们将在 第 13 章中描述的功能/em>,函数式编程。如果至少删除了一个元素,则返回true。boolean retainAll(Collection<?> c):这试图在集合中只保留提供的集合中包含的元素。类似于addAll()方法,如果至少保留一个元素,则此方法返回true;否则,它返回false。要使此方法正常工作,集合的每个元素和提供的集合的每个元素都必须实现equals()方法,并且对于设置,应该实现hashCode()方法。Object[] toArray(),T[] toArray(T[] a):这会将集合转换为数组。default T[] toArray(IntFunction:使用提供的函数将集合转换为数组。我们将在 第 13 章中解释函数, 函数式编程。 默认流<E> stream():这会返回一个Stream对象(我们在 第 14 章,Java 标准流)。默认流<E> parallelStream():这会返回一个可能并行的Stream对象(我们 在 第 14 章,Java 标准流)。
List interface
List 接口有 其他几个不属于其任何父接口的方法,如下所述:
- 静态工厂
of()方法,在 如何初始化 List 和 Set 小节中描述。 void add(int index, E element):这会将提供的元素插入到列表中提供的位置。静态列表<E> copyOf(Collection:这将返回一个不可修改的coll) List接口,其中包含给定Collection的元素接口并保留它们的顺序。以下代码片段演示了此方法的功能:E get(int index):这返回位于列表中指定位置的元素。列表<E> subList(int fromIndex, int toIndex):提取fromIndex(包括)和toIndex(不包括)之间的子列表.int indexOf(Object o):返回列表中指定元素的第一个索引(位置);列表中的第一个元素的索引(位置)为0。int lastIndexOf(Object o):返回列表中指定元素的最后一个索引(位置);列表中的最后一个元素具有list.size() - 1索引位置。E remove(int index):移除位于列表中指定位置的元素;它返回删除的元素。E set(int index, E element):替换位于列表中指定位置的元素;它返回被替换的元素。default void replaceAll(UnaryOperator:这通过将提供的函数应用于每个元素来转换列表。operator) UnaryOperator函数将在 第 13 章,函数式编程。ListIterator<E> listIterator():返回一个允许向后遍历列表的ListIterator对象。ListIterator<E> listIterator(int index):返回一个ListIterator对象,允许子列表(从提供的位置开始)为向后遍历。例如,请注意以下事项:default void sort(Comparator c):这个按照Comparator接口生成的顺序对列表进行排序假如。观察下面的,例如:
Comparable 接口只有一个 compareTo() 方法。在前面的例子中,我们在 类。如您所见,此实现提供与 Comparable 接口实现的基础上实现了 Comparator 接口">字符串Comparator.nullsFirst(Comparator.naturalOrder()) 相同的排序顺序。这种实现方式被称为函数式编程,我们将在第 13 章,函数式编程< /em>。
Set interface
- 静态
of()工厂方法,在 如何初始化 List 和 Set 小节中描述。 静态集合<E> copyOf(Collection方法:这将返回一个不可修改的 Set接口,其中包含给定Collection的元素;它的工作方式与static <E>列出<E> List interface 部分中描述的 copyOf(Collection方法。coll)
Map interface
int size()void clear()int hashCode()boolean isEmpty()布尔等于(Object o)default void forEach(BiConsumeraction) - 静态工厂方法:
of(),of(K, V v),of( K k1, V v1, K k2, V v2),以及除此之外的许多其他方法
但是,Map 接口不扩展 Iterable、Collection 或就此而言,任何其他界面。它被设计成能够通过它们的键存储值。每个键都是唯一的,而在同一张地图上可以使用不同的键存储多个相等的值。 key 和 value 的组合构成 Entry,它是 Map 的内部接口。 value 和 key 对象都必须实现 equals() 方法。 key 对象还必须实现 hashCode() 方法。
Map 接口的许多方法具有与 List 和 完全相同的签名和功能设置接口,这里不再赘述。我们将只介绍 Map 特定的方法,如下所示:
V get(Object key):根据提供的key取值;如果没有这样的键,它返回null。设置<K> keySet():这会从地图中检索所有键。集合<V> values():这会从地图中检索所有值。boolean containsKey(Object key):如果提供的键存在于地图中,则返回true。boolean containsValue(Object value):如果提供的值,则返回true存在于地图中。V put(K key, V value):这会将value和它的key添加到map中;它返回使用相同键存储的先前值。void putAll(Map<K,V> m):从地图中复制提供了所有的键值对。default V putIfAbsent(K key, V value):存储提供的值并映射到提供的键,如果这样的键还没有被映射使用。它返回映射到提供的键的值——现有的或新的。V remove(Object key):这会从地图中移除键和值;如果没有这样的键,或者该值为null,它会返回一个值或null。default boolean remove(Object key, Object value):如果映射中存在这样的键值对,则从映射中删除该键值对。default V replace(K key, V value):如果提供的键当前映射到提供的值,则替换值。如果被替换,则返回旧值;否则,它返回null。default boolean replace(K key, V oldValue, V newValue):这会将oldValue值替换为oldValue值,则提供literal">newValue 值。如果oldValue值被替换,则返回true;否则,它返回false。default void replaceAll(BiFunction:这个 将提供的函数应用到每个键值对在地图中并将其替换为结果,如果不可能,则抛出异常。 设置<Map.Entry<K,V>> entrySet():这将返回一组所有键值对作为Map.Entry的对象。default V getOrDefault(Object key, V defaultValue):这将返回映射到提供的键的值或defaultValue值,如果地图没有提供密钥。静态 Map.Entry<K,V> entry(K key, V value):这将返回一个不可修改的Map.Entry对象,其中包含key对象和value对象。静态地图<K,V> copy(Map这会将提供的 Map接口转换为不可修改的接口。
下面的 Map 方法对于本书的范围来说太复杂了,所以我们只是为了完整起见而提到它们。它们允许在 Map 接口中将多个值组合或计算并聚合为单个现有值,或者创建一个新值:
default V merge(K key, V value, BiFunction:如果提供的key-value对存在且value不是remappingFunction) null,提供的函数用于计算新值;如果新计算的值为null,它会删除键值对。如果提供的键值对不存在或值为null,则提供的非null值替换当前的值.此方法可用于聚合多个值;例如,它可用于连接以下字符串值:map.merge(key, value, String::concat)。我们将在 String::concat 的含义>第 13 章,函数式编程。default V compute(K key, BiFunction:使用提供的函数计算新值。remappingFunction) default V computeIfAbsent(K key, Function:仅当提供的键尚未与值关联时,才使用提供的函数计算新值,或 值为 null。default V computeIfPresent(K key, BiFunction<K,V,V> remappingFunction):仅当提供的键已与某个值关联时,才使用提供的函数计算新值并且值不是null。
最后一组 computing 和 merging 方法很少使用。到目前为止,最流行的是 V put(K key, V value) 和 V get(Object key) 方法,它允许使用主要的 Map 函数来存储键值对并使用键检索值。 Set<K> keySet() 方法通常用于遍历映射的键值对,尽管 entrySet() 方法似乎是一种更自然的方法。这是一个例子:
前面代码示例中的第一个 for 循环使用更广泛的方式通过遍历键来访问映射的键对值。第二个 for 循环遍历条目集,(在我们看来)这是一种更自然的方式。请注意,打印出的值与我们将它们放在地图中的顺序不同。这是因为,从 Java 9 开始,不可修改的集合(即 of() 工厂方法产生的)已经将随机化添加到 Set 的顺序 元素,在不同的代码执行之间改变元素的顺序。这样的设计是为了确保程序员不依赖于特定顺序的 Set 元素,这不能保证一套。
Unmodifiable collections
请注意,of() 工厂方法生成的 集合曾经是在 Java 9 中称为 immutable,在 Java 10 中称为 unmodifiable。这是因为 immutable 意味着您不能更改集合中的任何内容,而,实际上,如果集合元素是可修改的对象,则可以更改它们。例如,让我们构建一个 Person1 类的对象集合,如下所示:
在以下代码片段中,为简单起见,我们将创建一个仅包含一个元素的列表,然后尝试 修改该元素:
如您所见,虽然无法将元素添加到 of() factory 方法创建的列表中,如果对元素的引用存在于列表之外,则仍然可以修改其元素。
Collections utilities
有两个 类具有处理集合的静态方法,非常流行和有用,如下所示:
java.util.Collectionsorg.apache.commons.collections4.CollectionUtils
方法是静态的这一事实意味着它们不依赖于对象状态,因此它们是也称为无状态方法或实用方法。
java.util.Collections class
Collections 类 中的许多 方法管理集合以及分析、排序和比较它们。其中有 70 多个,所以我们没有机会谈论所有这些。相反,我们将看看主流应用程序开发人员最常用的那些,如下所示:
static copy(List<T> dest, List<T> src):这会将src列表的元素复制到 < code class="literal">dest 列表并保留元素的顺序及其在列表中的位置。dest列表大小必须等于或大于src列表大小,否则会引发运行时异常。这是此方法的用法示例:static void sort(List<T> list):根据实现的compareTo(T)方法对列表进行排序按每个元素(称为 自然排序)。它仅接受具有实现Comparable接口的元素的列表(这需要实现compareTo (T)方法)。在下面的示例中,我们使用List<String>因为String类实现了Comparable :
请注意,我们不能使用 List.of() 方法来创建列表,因为该列表是不可修改的,并且其顺序也无法更改。另外,查看生成的顺序:数字在前,然后是大写字母,然后是小写字母。这是因为 String 类中的 compareTo() 方法使用字符的代码点到 建立秩序。这是演示这一点的代码:
如您所见,顺序由组成字符串的字符的代码点的值定义。
static void sort(List<T> list, Comparator:这个按照 Comparator排序列表的顺序code> 对象提供,无论列表元素是否实现Comparable接口。例如,让我们对包含Person类中的对象的列表进行排序,如下所示:- 这里是
Comparator类,用于对列表进行排序="literal">Person对象:
现在,我们可以使用 Person 和 ComparePersons 类,如下所示:
正如我们已经提到的,Collections 类中有更多实用程序,因此我们建议您查看相关文档 at至少一次并了解其所有 功能。
CollectionUtils class
Apache Commons 项目中的 org.apache.commons.collections4.CollectionUtils 类 a>包含静态无状态方法,它们补充了 java.util.Collections 类的方法。它们有助于搜索、处理和比较 Java 集合。
要使用此类,您需要将以下依赖项添加到 Maven pom.xml 配置文件中:
这个类中有很多方法,随着时间的推移可能会添加更多的方法。这些实用程序是在 Collections 方法之外创建的,因此它们更加复杂和细微,不适合本书的范围。为了让您了解 CollectionUtils 类中可用的方法,下面是这些方法的简要说明,按功能分组:
- 从集合中检索元素的方法
- 将一个元素或一组元素添加到集合的方法
- 将
Iterable元素合并到集合中的方法 - 删除或保留有或没有条件的元素的方法
- 比较两个集合的方法
- 转换集合的方法
- 从集合中选择和过滤的方法
- 生成两个集合的并集、交集或差集的方法
- 创建不可变空集合的方法
- 检查集合大小和空的方法
- 反转数组的方法
最后一个方法应该属于处理数组的实用类,这就是我们现在要讨论的。
Arrays utilities
有两个 类具有处理集合的静态方法,非常流行和有用,如下所示:
java.util.Arraysorg.apache.commons.lang3.ArrayUtils
我们将简要回顾它们中的每一个。
java.util.Arrays class
我们 已经多次使用过 java.util.Arrays 类。它是阵列管理的主要实用程序类。由于 asList(T...a) 方法,这个实用程序类曾经非常流行。这是创建和初始化集合的最紧凑的方式,如下面的代码片段所示:
它仍然是创建可修改列表的一种流行方式——我们也使用过它。然而,在引入 List.of() 工厂方法后,Arrays 类大幅下降。
不过,如果您需要管理数组,那么 Arrays 类可能会有很大帮助。它包含 160 多个方法,其中大多数都使用不同的参数和数组类型进行了重载。如果我们按照方法名进行分组,将会有 21 组,如果我们进一步按功能分组,则只有以下 10 组将涵盖所有 Arrays 类的功能:
asList():这会根据提供的数组或逗号分隔的参数列表创建一个ArrayList对象。binarySearch():这会搜索一个数组或只搜索它的指定部分(根据索引的范围)。compare()、mismatch()、equals()和 < code class="literal">deepEquals():这些比较两个数组或其元素(根据索引的范围)。copyOf()和copyOfRange():这将复制所有数组或仅复制指定的(根据索引范围)部分他们。hashcode()和deepHashCode():这会根据提供的数组生成哈希码值。toString()和deepToString():这将创建一个String表示数组。fill()、setAll()、parallelPrefix()和 < code class="literal">parallelSetAll():这为数组的每个元素或根据索引范围指定的元素设置一个值(固定或由提供的函数生成)。sort()和parallelSort():对数组的元素或部分元素进行排序(根据范围指定指数)。splititerator():这将返回一个Splititerator对象,用于并行处理数组或其一部分(根据范围指定指数)。stream():这会生成数组元素或其中一些元素的流(根据索引范围指定);请参阅第 14 章,< em class="italic">Java 标准流.
所有这些方法都很有用,但我们想提请您注意 equals(a1, a2) 和 deepEquals(a1, a2) 方法。它们对数组比较特别有用,因为 array 对象不能实现 equals() 自定义方法,并且使用 equals() 的实现code class="literal">Object 类(仅比较引用)。 equals(a1, a2) 和 deepEquals(a1, a2)方法不仅可以比较 a1 和 a2 引用,还可以使用 equals() 方法来比较元素。这是代码示例,用于演示这些方法的工作原理:
可以看到,Arrays.deepEquals() 每次比较两个相等的数组时返回 true 当一个数组的每个元素都相等时另一个数组的元素在同一 位置,而 Arrays.equals() 方法 做同样的事情,但对于 一维 (1D) 数组 < /a>仅。
ArrayUtils class
org.apache.commons.lang3.ArrayUtils 类补充了 java.util.Arrays 类 通过向数组添加新方法管理工具包以及在以下情况下处理null的能力,否则, NullPointerException 可能会被抛出。要使用此类,您需要将以下依赖项添加到 Maven pom.xml 配置文件中:
ArrayUtils 类有大约 300 个重载方法,可分为以下 12 组:
add()、addAll()和insert():这些向数组中添加元素。clone():这个克隆一个数组,类似于的类和copyOf()方法Arraysjava.lang.System.的arraycopy()方法getLength():当数组本身为null 时,返回一个数组长度或0。hashCode():计算一个数组的哈希值,包括嵌套数组。contains()、indexOf()和lastIndexOf():这些搜索一个数组。isSorted()、isEmpty和isNotEmpty():这些检查数组和句柄null.isSameLength()和isSameType():这些比较数组。nullToEmpty():这会将null数组转换为空数组。remove(),removeAll(),removeElement(),removeElements()和removeAllOccurances():这些删除某些或所有元素。reverse()、shift()、shuffle()和< code class="literal">swap():这些改变数组元素的顺序。subarray():根据索引范围提取数组的一部分。toMap(),toObject(),toPrimitive(),toString()和toStringArray():这些 转换 将数组转换为另一种类型并处理null值。
Objects utilities
java.util.Objectsorg.apache.commons.lang3.ObjectUtils
它们在类创建过程中特别有用,因此我们将主要关注与此任务相关的方法。
java.util.Objects class
Objects 类只有 17 个方法,它们都是静态的。让我们在将它们应用到 Person 类时看看其中的一些。假设这个类是集合的一个元素,这意味着它必须实现 equals() 和 hashCode()方法。代码是 图解 在以下片段中:
请注意,我们不检查 null 的 name 属性,因为 Object.equals()<当任何参数为 null 时,/code> 不会中断。它只是完成比较对象的工作。如果其中只有一个是 null,则返回 false。如果两者都是 null,则返回 true。
使用 Object.equals() 是实现 equals() 方法的安全方式;但是,如果您需要比较可能是数组的对象,最好使用 Objects.deepEquals() 方法,因为它不仅处理 null,就像 Object.equals() 方法所做的那样,但也比较所有 数组元素的值,甚至如果数组是 多维的,如下所示:
Objects.hash() 方法也处理 null 值。要记住的一件重要事情是,在 equals() 方法中比较的属性列表必须与传递给 Objects.hash 的属性列表相匹配() 作为参数。否则,两个相等的 Person 对象将具有不同的哈希值,这使得基于哈希的集合无法正常工作。
还有一点值得注意的是,还有另外一个hash相关的Objects.hashCode()方法,它只接受一个参数,但是它生成的值不等于<代码 class="literal">Objects.hash() 只有一个参数。例如,请注意以下事项:
为避免此警告,请始终使用 Objects.hash()。
以下代码片段展示了另一个潜在的混淆来源:
如您所见,Objects.hashCode() 方法为 null 和 0,对于某些基于哈希值的 算法,这 可能会出现问题。
静态 <T> int compare (T a, T b, Comparator
0 (如果参数相等);否则,它返回
c.compare(a, b) 的结果。它对于实现
Comparable 接口(为自定义对象排序建立自然顺序)非常有用。例如,请注意以下事项:
这样,您可以通过设置Comparator.reverseOrder()轻松更改排序算法 值或通过添加 Comparator.nullFirst() 或 Comparator.nullLast()。
此外,我们在上一节中使用的 Comparator 实现可以通过使用 Objects.compare() 方法变得更加灵活,如下:
最后,我们将要讨论的 Objects 类的最后两个方法是生成对象的字符串表示的方法。当您需要在对象上调用 toString() 方法但不确定对象引用是否为 null。例如,请注意以下事项:
在 前面的 示例中,我们知道每个元素的确切值;但是,想象一下将列表作为参数传递给方法的场景。然后,我们被迫写这样的东西:
这似乎没什么大不了的。但是这样的代码写了十几遍之后,程序员自然会想到某种实用方法来完成所有这些,那就是 Objects 类的以下两个方法:
static String toString(Object o):返回参数不是toString()的结果class="literal">null 并在参数值为null时返回null。static String toString(Object o, String nullDefault):返回第一个参数调用toString()的结果不是null,当第一个参数值为null<时返回第二个nullDefault参数值/代码>。
以下代码片段演示了这两种方法:
截至编写时,Objects类有17个方法。我们建议您 熟悉它们,以避免在已经存在相同实用程序的情况下编写自己的实用程序。
ObjectUtils class
上一节的最后一个语句适用于org.apache.commons.lang3。 类,它补充了 Apache Commons 库的 ObjectUtilsjava.util.Objects 类中描述的方法前一节。本书的范围和分配的大小不允许详细回顾 ObjectUtils 类下的所有方法,因此我们将根据它们的相关功能分组简要介绍它们.要使用此类,您需要将以下依赖项添加到 Maven pom.xml 配置文件中:
- 对象克隆方法
- 支持比较两个对象的方法
notEqual()方法,比较两个对象的不等式,其中一个对象或两个对象都可能为null- 几个
identityToString()方法生成所提供对象的String表示,就好像由生成toString(),这是Object基类的默认方法,并且可以选择将其附加到另一个对象 allNotNull()和anyNotNull()方法,用于分析null 的对象数组firstNonNull()和defaultIfNull()方法,它们分析对象数组并返回第一个 not-null对象或默认值max()、min()、median()和mode()方法,分析一组 对象并返回对应于 的对象方法名
The java.time package
java.time 包及其子包中有很多类。它们被引入作为处理日期和时间的其他(旧包)的替代品。新类是线程安全的(因此,更适合多线程处理),同样重要的是它们的设计更加一致且更易于理解。此外,新实施遵循 国际标准化组织 (ISO) 标准至于日期和时间格式,但也允许使用任何其他自定义格式。
我们将描述以下五个主要类并演示如何使用它们:
java.time.LocalDatejava.time.LocalTimejava.time.LocalDateTimejava.time.Periodjava.time.Duration
java.time 包的所有这些和其他类,以及它的子包,都具有涵盖所有实际案例的丰富功能。但我们不会讨论所有这些。我们将只介绍基础知识和最流行的用例。
LocalDate class
LocalDate 类 不带时间。它表示 ISO 8601 格式的 日期(yyyy-MM-dd ) 并显示在以下代码片段中:
这是撰写本文时该位置的当前日期。该值是从计算机时钟中获取的。同样,您可以使用该静态 now(ZoneId zone) 方法获取任何其他时区的当前日期。 ZoneId 对象可以使用静态 ZoneId.of(String zoneId) 方法构造,其中 String zoneId 是 ZoneId.getAvailableZoneIds() 方法返回的任何字符串值,如以下代码片段所示:
前面的 代码 打印近 600 个时区 标识符 (ID)。这里有几个:
让我们尝试 到 使用 "Asia/Tokyo",例如,如下:
LocalDate 对象可以使用以下方法表示过去或将来的任何日期:
LocalDate parse(CharSequence text):从 ISO 8601 格式的字符串(yyyy-MM-dd)。LocalDate parse(CharSequence text, DateTimeFormatter formatter):这会从DateTimeFormatter对象指定格式的字符串构造一个对象,拥有丰富的模式系统和许多预定义的格式——以下是其中的一些:BASIC_ISO_DATE——例如,20111203ISO_LOCAL_DATE ISO——例如,2011-12-03ISO_OFFSET_DATE——例如,2011-12-03+01:00ISO_DATE——例如,2011-12-03+01:00; 2011-12-03ISO_LOCAL_TIME——例如,10:15:30ISO_OFFSET_TIME——例如,10:15:30+01:00ISO_TIME——例如,10:15:30+01:00; 10:15:30ISO_LOCAL_DATE_TIME——例如,2011-12-03T10:15:30
LocalDate of(int year, int month, int dayOfMonth):从年、月、日构造一个对象。LocalDate of(int year, Month, int dayOfMonth):这会从年、月(枚举常量)和日构造一个对象。LocalDate ofYearDay(int year, int dayOfYear):这会根据年份和年份构造一个对象。
以下代码片段演示了上述项目符号中列出的方法:
一个LocalDate对象可以提供各种值,如如下代码所示片段:
可以比较 LocalDate 对象,如下所示:
LocalDate 类中有 很多 其他有用的方法。如果您必须使用日期,我们建议您阅读应用程序编程接口 (该类的API)和java.time包及其子包的其他类。
LocalTime class
LocalTime 类 包含没有日期的时间。它具有与 LocalDate 类的方法类似的 方法。下面是如何创建 LocalTime 类的对象:
时间值的每个分量都可以从 LocalTime 对象中提取,如下所示:
并且LocalTime类的两个对象也可以进行比较,如下:
LocalTime 类中还有 许多其他有用的方法。如果您 必须使用日期,我们建议您阅读该类的 API 和 java.time 的其他类包及其子包。
LocalDateTime class
LocalDateTime 类 包含日期和时间,并具有所有方法LocalDate 和 LocalTime 类都有,这里不再赘述。我们将只展示如何创建 LocalDateTime 类的对象,如下所示:
LocalDateTime 类中还有许多其他有用的方法。如果您必须使用 日期,我们建议您阅读此 类和java.time 包及其子包。
Period and Duration classes
java.time.Period 和 java.time.Duration 类旨在包含< /a> 时间,如 所述:
Period对象包含以年、月和日为单位的时间量。Duration对象包含以小时、分钟、秒和纳秒为单位的时间量。
以下代码片段演示了使用 LocalDateTime 类创建和使用它们的方法,但 LocalDate 中存在相同的方法(对于 Period) 和 LocalTime(对于 Duration)类:
以下方法的工作方式与 LocalTime 类的方法相同:
其他方法 a>创建和使用 Period 对象在以下代码片段中演示:
Duration 对象可以类似地创建和使用,如下面的代码片段所示:
还有有很多其他 a>Period 和 Duration 类中的有用方法。如果您必须使用日期,我们建议您阅读该类的 API 以及 java.time 包及其子包的其他类。
Period of day
Java 16 包含一种 新时间格式,该格式将一天的时间段显示为AM
、早上等。以下两个方法演示了 DateTimeFormatter.ofPattern() 方法与 LocalDateTime 和 的用法LocalTime 类:
您可以 使用 "ha" 和 "h B" 模式来制作时间呈现更人性化。
Summary
本章向您介绍了 Java 集合框架及其三个主要接口:List、Set 和 地图。讨论了每个接口,并用其中一个实现类演示了它的方法。泛型也得到了解释和演示。必须实现 equals() 和 hashCode() 方法才能使对象能够被 Java 处理正确收藏。
Collections 和 CollectionUtils 实用程序类具有许多有用的收集处理方法,并在示例中与 数组、ArrayUtils、Objects和ObjectUtils
类。
java.time 包的类方法允许管理时间/日期值,并在特定的实用代码片段中进行了演示。
您现在可以在程序中使用我们在本章中讨论的所有主要数据结构。
在下一章中,我们将概述 JCL 和一些外部库,包括那些支持测试的库。具体来说,我们将探索 org.junit、org.mockito、org.apache。 log4j、org.slf4j 和 org.apache.commons 包及其子包。
Quiz
- 什么是 Java 集合框架?选择所有符合条件的:
- 一系列框架
java.util包的类和接口List、Set、Map接口- 实现集合数据结构的类和接口
- 集合中的泛型是什么意思?选择所有符合条件的:
- 集合结构定义
- 元素类型声明
- 类型泛化
- 一种提供编译时安全性的机制
of()工厂方法的集合有什么限制?选择所有符合条件的:- 它们不允许
null元素。 - 它们不允许将元素添加到已初始化的集合中。
- 它们不允许修改与已初始化集合相关的元素。
- 它们不允许
java.lang.Iterable接口的实现允许什么?选择所有符合条件的:- 它允许一个一个地访问集合的元素。
- 它允许在
FOR语句中使用集合。 - 它允许在
WHILE语句中使用集合。 - 它允许在
DO...WHILE语句中使用集合。
java.util.Collection接口的实现允许什么?选择所有符合条件的:- 添加到另一个集合中的元素集合
- 从属于另一个集合元素的对象集合中移除
- 仅修改集合中属于另一个集合的元素
- 从不属于另一个集合的对象集合中删除
- 选择与
List接口方法有关的所有正确语句:z get(int index):返回列表中指定位置的元素。E remove(int index):删除列表中指定位置的元素;它返回被移除的元素。静态列表<E> copyOf(Collection:这将返回一个不可修改的coll) List接口,其中包含给定Collection接口的元素并保持他们的秩序。int indexOf(Object o):返回指定元素在列表中的位置。
- 选择与
Set接口方法有关的所有正确语句:E get(int index):返回列表中指定位置的元素。E remove(int index):删除列表中指定位置的元素;它返回被移除的元素。静态集合<E> copyOf(Collection:这将返回一个不可修改的coll) Set接口,其中包含给定Collection接口的元素.int indexOf(Object o):返回指定元素在列表中的位置。
- 选择与
Map接口方法相关的所有正确语句:int size():返回map中存储的键值对的数量;当isEmpty()方法返回true时,该方法返回0。V remove(Object key):这会从映射中移除键和值;如果没有这样的键或值为null,则返回value或null>.default boolean remove(Object key, Object value):如果映射中存在这样的键值对,则删除该键值对;如果值被删除,则返回true。default boolean replace(K key, V oldValue, V newValue):这会将oldValue值替换为oldValue 值,则提供 class="literal">newValue 值——它返回true 如果oldValue值被替换;否则,它返回false。
- 选择与
Collectionsstatic void sort(List<T> list, Comparator<T>comparator) 方法有关的所有正确语句> 类:- 如果列表元素实现了
Comparable接口,它将对列表的自然顺序进行排序。 - 它根据提供的
Comparator对象对列表的顺序进行排序。 - 如果列表元素实现
Comparable接口,它会根据提供的Comparator对象对列表的顺序进行排序。 - 它根据提供的
Comparator对象对列表的顺序进行排序,而不管列表元素是否实现Comparable接口。< /li>
- 如果列表元素实现了
- 执行以下代码的结果是什么?
[s1, s2, s3, s4][s3, s4, s3][s1, s2, s3, s3, s4][s3, s4]
CollectionUtils类方法的功能是什么?选择所有符合条件的:- 它匹配
Collections类方法的功能,但是通过处理null - 它补充了
Collections类方法的功能 - 它以
Collections类方法不具备的方式搜索、处理和比较 Java 集合 - 它复制了
Collections类方法的功能
- 它匹配
- 执行以下代码的结果是什么?
false true假真假真
- 执行以下代码的结果是什么?
1 2 0 false true2 1 1 false true2 1 0 false true2 1 0 真假
- 执行以下代码的结果是什么?
真 0 0错误false -1 0false 31 0
- 执行以下代码的结果是什么?
c x aa c xx c aa x c
- 执行以下代码的结果是什么?
1921-02-2321-02-230021-02-23错误
- 执行以下代码的结果是什么?
20:23:12.00000030020:23:12.30020:23:12:300错误
- 执行以下代码的结果是什么?
2020-02-23 20:23:122020-02-23T20:23:122020-02-23:20:23:12错误
- 执行以下代码的结果是什么?
2020-02-23T20:23:12 2020-02-23T20:23:122020-02-23T20:23:12 2020-02-23T20:35:122018-02-23T20:23:12 2020-02-23T20:35:12 2020-02-23T20:23:122018-02-23T20:23:12 2020-02-23T20:35:12 2018-02-23T20:35:12
