Java中,stream()
方法使用汇总
目标
- 通过示例学习,掌握Java
stream()
方法的实践应用
在Java中,stream()
方法用于将集合(如List、Set等)或数组转换为Stream流对象,以便进行各种流式操作。流(Stream)提供了一种高效且声明式的方式来处理数据集合。使用流,你可以对集合中的元素执行一系列复杂的操作,如过滤、映射、排序、聚合等,而无需编写繁琐的循环代码。
1. 过滤(Filter)
使用filter()
方法可以根据条件筛选流中的元素。
import org.junit.Test;
import static org.junit.Assert.*;
import java.util.stream.Stream;
import java.util.function.Predicate;
public class StreamTest {
@Test
public void testFilter() {
// Create a stream of integers
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
// Define a predicate to filter even numbers
Predicate<Integer> isEven = n -> n % 2 == 0;
// Filter the stream using the predicate
Stream<Integer> filteredStream = stream.filter(isEven);
// Convert the stream to an array for assertion
Integer[] filteredArray = filteredStream.toArray(Integer[]::new);
// Assert that the filtered array contains only even numbers
assertArrayEquals(new Integer[]{2, 4}, filteredArray);
}
}
2. 映射(Map)
使用map()
方法可以将流中的元素转换为其他对象或类型。
List<String> strings = Arrays.asList("apple", "banana", "cherry");
List<Integer> stringLengths = strings.stream()
.map(String::length)
.collect(Collectors.toList());
3. 排序(Sort)
使用sorted()
方法可以对流中的元素进行排序。
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6, 5);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
4. 聚合(Reduce)
使用reduce()
方法可以将流中的元素聚合为一个单独的值。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
int total = sum.orElseThrow(); // 或者 sum.get() 如果确定流不为空
5. 查找(Find)
使用findAny()
或findFirst()
方法可以从流中找到一个元素。
List<String> strings = Arrays.asList("apple", "banana", "cherry");
Optional<String> firstString = strings.stream()
.findFirst();
6. 分组(Grouping)
使用collect(Collectors.groupingBy(...))
方法可以根据某个条件对流中的元素进行分组。
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 25),
new Person("David", 35)
);
Map<Integer, List<Person>> peopleByAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
7. 连接(Joining)
使用collect(Collectors.joining(...))
方法可以将流中的字符串元素连接成一个单独的字符串。
List<String> words = Arrays.asList("Hello", "world", "!");
String sentence = words.stream()
.collect(Collectors.joining(" "));
8. 匹配(Matching)
使用anyMatch()
, allMatch()
, 和 noneMatch()
方法可以对流中的元素进行匹配操作,以检查是否至少有一个、所有或没有元素满足某个条件。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0); // 至少有一个偶数
9. 数值操作(Numeric Operations)
对于数值流(如IntStream
, LongStream
, DoubleStream
),可以使用sum()
, average()
, max()
, min()
等方法进行数值操作。
IntStream numbers = IntStream.of(1, 2, 3, 4, 5);
int sum = numbers.sum();
OptionalDouble average = numbers.average();
10. 扁平化(Flattening)
使用flatMap()
方法可以将流中的元素转换为其他流,并将这些流中的所有元素合并成一个流。
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.stream.Stream;
import java.util.function.Function;
public class StreamTest {
@Test
public void testFlatMap() {
Stream<Integer> stream = Stream.of(1, 2, 3);
Function<Integer, Stream<String>> mapper = i -> Stream.of("a", "b", "c");
Stream<String> result = stream.flatMap(mapper);
assertNotNull(result);
assertEquals(9, result.count());
assertTrue(result.anyMatch("a"::equals));
assertTrue(result.anyMatch("b"::equals));
assertTrue(result.anyMatch("c"::equals));
}
@Test
public void testFlatMapWithNullMapper() {
Stream<Integer> stream = Stream.of(1, 2, 3);
Function<Integer, Stream<String>> mapper = null;
Exception exception = assertThrows(NullPointerException.class, () -> {
stream.flatMap(mapper);
});
assertEquals("mapper cannot be null", exception.getMessage());
}
@Test
public void testFlatMapWithEmptyStream() {
Stream<Integer> stream = Stream.empty();
Function<Integer, Stream<String>> mapper = i -> Stream.of("a", "b", "c");
Stream<String> result = stream.flatMap(mapper);
assertNotNull(result);
assertTrue(result.isEmpty());
}
}
11、 List转map 键(key)不重复
在Java中,将List转换为Map通常涉及到将List中的元素按照某种规则映射到Map的键值对上。这通常涉及到List中的对象具有可以作为键(key)和值(value)的属性。以下是一个简单的示例,说明如何将一个包含自定义对象的List转换为Map。
假设我们有一个Person
类,它有两个属性:name
和age
。
public class Person {
private String name;
private int age;
// 构造器、getter和setter方法省略...
}
现在,假设我们有一个Person
对象的List,并且我们想要将这个List转换为一个Map,其中键是name
,值是对应的Person
对象。我们可以使用Java 8的Stream API来实现这个转换:
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Person> people = List.of(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
);
Map<String, Person> peopleMap = people.stream()
.collect(Collectors.toMap(Person::getName, person -> person));
// 打印Map的内容
peopleMap.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));
}
}
12、List转map 键(key)重复
当使用Java 8的Stream API将List转换为Map,并需要自定义处理重复键时,你可以利用Collectors.toMap
方法,并传入一个自定义的合并函数来处理键冲突。下面是一个示例,演示了如何使用Stream API以及自定义的合并逻辑来处理重复的键:
假设我们有一个员工列表,每个员工都有一个ID和姓名,我们希望将这些员工转换为一个Map,其中键是员工的ID,值是员工的姓名。如果遇到重复的ID,我们将所有具有相同ID的员工的姓名连接成一个字符串。
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
public class ListStreamToMapExample {
public static void main(String[] args) {
// 假设我们有一个员工列表,其中可能包含重复的员工ID
List<Employee> employees = Arrays.asList(
new Employee("1", "Alice"),
new Employee("2", "Bob"),
new Employee("1", "Charlie"), // 重复的ID
new Employee("3", "David")
);
// 我们想要将这些员工转换为一个Map,其中键是员工的ID,值是员工的姓名
// 如果遇到重复的ID,我们将所有具有相同ID的员工的姓名连接成一个字符串
Map<String, String> employeeMap = employees.stream()
.collect(Collectors.toMap(
Employee::getId, // 键映射函数
Employee::getName, // 值映射函数
(name1, name2) -> name1 + ", " + name2 // 合并函数,处理重复的键
));
// 打印结果以验证
employeeMap.forEach((id, name) -> System.out.println("ID: " + id + ", Name: " + name));
}
// 员工类,包含ID和姓名
static class Employee {
private String id;
private String name;
public Employee(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
// 根据需要重写equals和hashCode方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
}
在这个例子中,Collectors.toMap
接收三个参数:
- 键映射函数(
Employee::getId
),它定义了如何从流中的元素提取键。 - 值映射函数(
Employee::getName
),它定义了如何从流中的元素提取值。 - 合并函数(
(name1, name2) -> name1 + ", " + name2
),它定义了当遇到重复的键时如何合并值。在这个例子中,我们将两个姓名连接成一个字符串,用逗号和空格分隔。
运行这段代码后,你将得到一个Map,其中重复的ID对应的值是所有具有该ID的员工的姓名连接而成的字符串。例如,ID为"1"的键对应的值将是"Alice, Charlie"。