Modify and Print List Items With Java Streams – 使用 Java 流修改和打印列表项

最后修改: 2024年 3月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

When we work with Java, manipulating lists is a fundamental skill.

在使用 Java 时,操作列表是一项基本技能。

In this quick tutorial, we’ll explore different ways to modify or transform a list and then print its elements in Java.

在本快速教程中,我们将探索修改或转换列表的不同方法,然后用 Java 打印其元素。

2. Modifying and Printing a List

2.修改和打印列表

Printing a list of elements isn’t a challenge to us. For example, we can call the print action in the forEach() method:

打印元素列表对我们来说并非难事。例如,我们可以在 forEach() 方法中调用打印操作

List<String> theList = Lists.newArrayList("Kai", "Liam", "Eric", "Kevin");
theList.forEach(element -> log.info(element));

In the code above, we used an SLF4J logger to output elements in the given list. When we execute the code, we can see the four names are printed in the console:

在上面的代码中,我们使用了 SLF4J 日志记录器来输出给定列表中的元素。执行代码时,我们可以看到控制台中打印出了四个名称:

Kai
Liam
Eric
Kevin

If we intend to modify the elements in the list before printing them, we can utilize the List.replaceAll() method.

如果我们打算在打印之前修改列表中的元素,我们可以使用 List.replaceAll() 方法。如果我们打算在打印之前修改列表中的元素,我们可以使用 List.replaceAll() 方法。

Next, let’s convert each String element in theList to uppercase and print the modified values in a test method:

接下来,让我们将 theList 中的每个 String 元素转换为大写,并在测试方法中打印修改后的值:

List<String> theList = Lists.newArrayList("Kai", "Liam", "Eric", "Kevin");
theList.replaceAll(element -> element.toUpperCase());
theList.forEach(element -> log.info(element));
assertEquals(List.of("KAI", "LIAM", "ERIC", "KEVIN"), theList);

As we can see, we use a lambda expression in the replaceAll() method to perform the case conversion. After running the test, we can see the uppercase values in the console:

正如我们所见,我们在 replaceAll() 方法中使用 lambda 表达式来执行大小写转换。运行测试后,我们可以在控制台中看到大写值:

KAI
LIAM
ERIC
KEVIN

It’s worth noting that the replaceAll() method requires the list object to be a mutable list, such as the Arraylist used in the above code. The method throws an UnsupportedOperationException if the list is immutable, such as the list objects returned by Collection.singletonList() and List.of().

值得注意的是,replaceAll() 方法要求列表对象为可变列表,例如上述代码中使用的 Arraylist如果列表不可变,例如 Collection.singletonList() 返回的列表对象,该方法将抛出UnsupportedOperationException。singletonList()List.of() 所返回的列表对象。

Therefore, in practical scenarios, it’s often preferable to transform the original list into a new one rather than directly modifying it. Next, let’s explore how to transform a list and seamlessly output its elements efficiently.

因此,在实际应用中,通常最好是将原始列表转换成新列表,而不是直接对其进行修改。接下来,让我们来探讨如何有效地转换列表并无缝输出其元素。

3. Transforming and Printing a List Using the Stream API

3.使用流 API 转换和打印列表

The Stream API, introduced in Java 8, significantly changed the way we handle collections of objects. Streams provide a declarative and functional approach to processing data, offering a concise and expressive way to perform operations on collections.

Java 8 中引入的 Stream API 显著改变了我们处理对象集合的方式。Streams 提供了一种声明式和函数式的数据处理方法,为在集合上执行操作提供了一种简洁而富有表现力的方式。

For example, we can take a list as the source, use the map() method to transform elements in the stream, and print the elements using forEachOrdered() in this way:

例如,我们可以将一个列表作为源,使用 map() 方法转换流中的元素,然后使用 forEachOrdered() 方法打印元素:

theList.stream()
  .map(... <the transformation logic> ...)
  .forEachOrdered( ... <print the element> ...)

The code is pretty straightforward. However, it’s important to note that Stream.forEachOrdered() is a terminal operation. This terminal operation essentially marks the end of the stream pipeline. Consequently, the stream object becomes inaccessible after this method is called. This limitation implies that subsequent stream operations, such as collecting the transformed elements, are no longer feasible.

代码非常简单。不过,需要注意的是,Stream.forEachOrdered() 是一个终端操作。该终端操作本质上标志着流 管道的结束。因此,在调用此方法后,流对象将变得不可访问。这一限制意味着后续的流操作(如收集转换后的元素)不再可行

Therefore, we’d like to achieve our goal through a different approach, one that allows us to continue performing operations on the stream.

因此,我们希望通过一种不同的方法来实现我们的目标,这种方法可以让我们继续对数据流执行操作。

A straightforward idea is to include the printing method call in map():

一个直接的想法是在 map() 中包含打印方法调用:

List<String> theList = List.of("Kai", "Liam", "Eric", "Kevin");
List<String> newList = theList.stream()
  .map(element -> {
      String newElement = element.toUpperCase();
      log.info(newElement);
      return newElement;
  })
  .collect(Collectors.toList());
assertEquals(List.of("KAI", "LIAM", "ERIC", "KEVIN"), newList);

In this way, printing the stream doesn’t terminate the stream pipeline, and we can still perform a Collector operation afterward. Of course, the transformed elements are printed in the console:

这样,打印流并不会终止流管道,我们仍然可以在打印后执行 Collector 操作。当然,转换后的元素会打印到控制台中:

KAI
LIAM
ERIC
KEVIN

However, one drawback of this approach is that it unnecessarily adds irrelevant logic to the map() method. Next, let’s improve it by employing the peek() method:

但是,这种方法的一个缺点是,它不必要地在 map() 方法中添加了无关逻辑。接下来,让我们使用 peek() 方法对其进行改进:

List<String> theList = List.of("Kai", "Liam", "Eric", "Kevin");
List<String> newList = theList.stream()
  .map(element -> element.toUpperCase())
  .peek(element -> log.info(element))
  .collect(Collectors.toList());
assertEquals(List.of("KAI", "LIAM", "ERIC", "KEVIN"), newList);

Unlike forEachOrdered(), peek() is an intermediate operation. It performs the provided action on each element in the stream and returns the stream. Therefore, we can add further operations to the stream pipeline after invoking peek(), such as collect() in the above code.

forEachOrdered() 不同,peek() 是一个中间操作。<因此,我们可以在调用 peek() 之后向流管道添加更多操作,例如上述代码中的 collect()

The peek() method accepts a Consumer instance as the parameter. In our example, we passed a lambda expression as the Consumer to peek().

peek() 方法接受一个 Consumer 实例作为参数。在我们的示例中,我们将一个 lambda 表达式作为 Consumer 传递给了 peek(). 方法。

When we give this test a run, it passes, and the expected output is printed to the console:

当我们运行这个测试时,它通过了,预期的输出被打印到控制台:

KAI
LIAM
ERIC
KEVIN

4. Conclusion

4.结论

In this article, we first demonstrated how to modify and print a list using the replaceAll() + forEach() approach. Then, we explored how to use the Stream API to transform and print elements in a stream.

在本文中,我们首先演示了如何使用 replaceAll() + forEach() 方法修改和打印列表。然后,我们探讨了如何使用流 API 转换和打印流中的元素。

As always, the complete source code for the examples is available over on GitHub.

与往常一样,这些示例的完整源代码可在 GitHub 上获取。