1. 概述

在Java编程中,操作列表是基础技能之一。

本快速教程将介绍几种修改或转换列表的方法,并演示如何在Java中打印其元素。

2. 修改并打印列表

打印列表元素对我们来说并不困难。例如,我们可以使用forEach()方法调用打印动作:

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

在这段代码中,我们使用了SLF4J日志器输出给定列表中的元素。执行代码后,我们在控制台能看到四个名字:

Kai
Liam
Eric
Kevin

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

接下来,让我们在测试方法中将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);

如图所示,我们在replaceAll()方法中使用了一个lambda表达式来执行大小写转换。运行测试后,我们能在控制台看到大写值:

KAI
LIAM
ERIC
KEVIN

需要注意的是,replaceAll()方法要求列表对象是可变的,如上述代码中使用的ArrayList。如果列表是不可变的,比如由Collections.singletonList()List.of()返回的对象,该方法会抛出UnsupportedOperationException

因此,在实际场景中,通常更倾向于将原始列表转换为新的列表,而不是直接修改它。接下来,我们将探讨如何高效地转换列表并流畅地输出其元素。

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

Java 8引入的流API(Stream API)极大地改变了我们处理对象集合的方式。流提供了一种声明式和函数式的数据处理方法,为对集合进行操作提供了简洁且表达性强的方式。

例如,我们可以从列表开始,使用map()方法对流中的元素进行变换,然后使用forEachOrdered()打印元素,如下所示:

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

这段代码相当直观。但重要的是要注意,Stream.forEachOrdered()是一个终端操作。这个终端操作本质上标志着流的管道结束。因此,调用此方法后,流对象就无法访问了。这意味着后续的流操作,如收集变换后的元素,不再可行。

因此,我们希望通过不同的方式实现目标,允许我们在流上继续执行操作。

一个简单的方法是在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);

这样,打印流并不会终止流的管道,我们仍可以在之后进行如collect()这样的操作。当然,变换后的元素会被打印到控制台:

KAI
LIAM
ERIC
KEVIN

然而,这种方法的一个缺点是,它在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);

forEachOrdered()不同,peek()是一个中间操作。它对流中的每个元素执行提供的操作并返回流。因此,我们在调用peek()后,还可以在流管道中添加更多的操作,如上述代码中的collect()

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

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

KAI
LIAM
ERIC
KEVIN

4. 总结

本文首先展示了如何使用replaceAll() + forEach()方法修改并打印列表。接着,我们探讨了如何使用流API来转换并打印流中的元素。

如往常一样,示例代码的完整源码可在GitHub上找到。