filter()
filter 方法接受一个 Predicate 函数式接口,该接口定义了一个测试条件。流中的每个元素都会通过这个测试条件,只有那些满足条件的元素会被保留在过滤后的流中。
语法:
CodeStream<T> filter(Predicate<? super T> predicate);
ps:
List<Event> evenList = Arrays.asList(
new Event("C", "迷茫"),
new Event("L", "迷茫"),
new Event("Z", "认真"),
new Event("M", "开心"),
new Event("W", "悲伤")
);
// 输出 Event{name='c', status='迷茫'} Event{name='l', status='迷茫'}
evenList.stream().filter(e -> e.getStatus().equals("迷茫")).collect(Collectors.toList()).forEach(System.out::println);
特性:
- 中间操作:
filter是一个中间操作,这意味着它返回一个新的流,原始流不受影响。多个中间操作可以链式调用。 - 惰性求值:
filter操作在流的终端操作(如collect)执行之前不会实际执行。 - 不改变原始数据:
filter不会改变原始的集合数据,它只是创建了一个新的流来包含符合条件的元素。
map()
语法:
Code<R> Stream<R> map(Function<? super T, ? extends R> mapper);
ps:
List<Event> eventList = Arrays.asList(
new Event("C", 1),
new Event("L", 1),
new Event("Z", 3),
new Event("W", 2),
new Event("M", 3)
);
// 输出 C L Z W M
eventList.stream().map(Event::getName).collect(Collectors.toList()).forEach(System.out::println);
特性
- 中间操作:
map是一个中间操作,返回一个新的流,原始流不受影响。多个中间操作可以链式调用。 - 惰性求值:
map操作在流的终端操作(如collect)执行之前不会实际执行。 - 转换数据:
map不会修改原始数据结构,它会生成一个新的流,其中包含转换后的元素。
常见用法
- 对象转换: 将对象的属性从一个形式转换为另一个形式。例如,将字符串转换为其长度。
- 数据格式转换: 将数据转换为另一种格式。例如,将数字格式化为字符串。
- 计算: 对流中的元素进行计算,例如计算每个数的平方、增加一定的值等。
sorted(Comparator.comparing())
语法
sorted()
CodeStream<T> sorted(Comparator<? super T> comparator);
语法:
Comparator.comparing() 是 Comparator 接口的一个静态方法,用于创建一个比较器,该比较器根据对象的某个特定属性进行排序。通常用法是先通过 Comparator.comparing() 创建一个比较器,再将其传递给 sorted() 方法。
Codestatic <T, U> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator);
keyExtractor是一个函数,用于提取用于比较的键。keyComparator是一个比较器,用于比较键。如果不提供keyComparator,则默认按自然顺序进行排序。
假设我们有一个字符串列表,我们想要按照字符串长度进行排序:
List<String> words = Arrays.asList("01", "012345", "01234", "0123", "012");
/**
* 输出
* 01
* 012
* 0123
* 01234
* 012345
*/
words.stream().sorted(Comparator.comparing(String::length)).collect(Collectors.toList()).forEach(System.out::println);
sorted().limit()
limit(long maxSize) 是一个终端操作,用于限制流中返回的元素数量。它会返回一个包含流前 maxSize 个元素的新流。注意,limit() 在实际计算流时会应用截断,所以它对流的性能和结果有实际影响。
比如 按 num 属性从大到小排序后的前三个事件:
List<Event> list = Arrays.asList(
new Event("C", "1", 2),
new Event("L", "1", 3),
new Event("Z", "2", 1),
new Event("W", "4", 2),
new Event("M", "2", 4)
);
List<Event> a2 = list.stream().sorted((e1,e2) -> Integer.compare(e2.getNum(),e1.getNum())).limit(3).collect(Collectors.toList());
//[Event{name='M', num=4}, Event{name='L', num=3}, Event{name='C', num=2}]
reduce()
reduce() 方法有两种形式:
reduce(BinaryOperator<T> accumulator):接收一个累积函数,对流中的元素进行累积,最终得到一个Optional<T>结果。reduce(T identity, BinaryOperator<T> accumulator):接收一个初始值和一个累积函数,进行累积操作,最终返回一个结果类型为T的值。
假设我们有一个整数列表,我们想要计算所有元素的总和:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println("Sum: " + sum); // 输出:Sum: 15
假设我们有一个整数列表,想找到其中的最大值:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> max = numbers.stream()
.reduce((a, b) -> a > b ? a : b);
max.ifPresent(System.out::println); // 输出: 5
总结
reduce()用于将流中的元素进行累积操作,得到最终结果。- 形式
reduce(BinaryOperator<T> accumulator)返回Optional<T>,适用于没有初始值的情况。 - 形式
reduce(T identity, BinaryOperator<T> accumulator)返回类型T的值,适用于有初始值的情况。 - 常见用例包括计算总和、找最大值、拼接字符串等。
anyMatch()
语法
Codeboolean anyMatch(Predicate<? super T> predicate)
假设我们有一个整数列表,想要检查是否包含任何负数:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 检查是否有负数
boolean hasNegative = numbers.stream()
.anyMatch(n -> n < 0);// 输出: false
假设我们有一个字符串列表,想要检查是否包含某个特定的字符串:
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 检查是否包含"banana"
boolean containsBanana = words.stream()
.anyMatch(word -> word.equals("banana"));
System.out.println(containsBanana); // 输出: true
总结
anyMatch()用于检查流中是否有至少一个元素满足给定的条件。- 如果流中存在满足条件的元素,
anyMatch()会返回true,否则返回false。 - 这是一个短路操作,一旦找到符合条件的元素,就会停止后续的处理,提高效率。
flatMap()
语法
Code<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
flatMap() 方法用于处理嵌套的流,将多个流合并为一个流。以下是一个示例:
List<String> sentences = Arrays.asList("这里是", "不爱", "吃鱼");
// 使用 flatMap 将每个句子拆分成单词,并将所有单词扁平化到一个流中
List<String> words = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.collect(Collectors.toList());
// 输出: [这里是, 不爱, 吃鱼]
假设我们有一个用户列表,每个用户有一个订单列表。我们想要提取所有用户的所有订单:
List<User> users = Arrays.asList(
new User("高木", Arrays.asList(new Order("苹果"), new Order("香蕉"))),
new User("西片", Arrays.asList(new Order("西瓜"), new Order("哈密瓜")))
);
// 使用 flatMap 提取所有订单
List<Order> allOrders = users.stream()
.flatMap(u -> u.orders.stream())
.collect(Collectors.toList());
System.out.println(allOrders); // 输出: [苹果, 香蕉, 西瓜, 哈密瓜]
总结
flatMap()用于将流中的每个元素映射成一个流,并将所有这些流的元素扁平化成一个单一的流。- 与
map()方法不同,flatMap()允许每个元素映射成零个或多个元素,从而提供了更强的灵活性。 - 常用于处理嵌套集合或需要将多个流合并成一个流的场景。
peek()
语法
CodeStream<T> peek(Consumer<? super T> action)
peek() 方法允许在流的每个元素上执行操作,但不会改变流中元素的类型或数量。它通常用于调试或记录流处理过程中的中间状态,或者对流中的元素进行某种副作用操作。
假设有一个字符串列表,我们希望在转换成大写之前输出每个字符串的长度:
List<String> words = Arrays.asList("apple", "banana", "orange");
List<String> capitalizedWords = words.stream()
.peek(s -> System.out.println( s + ": " + s.length()))
.map(String::toUpperCase)
.collect(Collectors.toList()); //流转列表
// capitalizedWords输出为:apple: 5 banana: 6 orange: 6 [APPLE, BANANA, ORANGE]
注意
peek()不会修改流的元素,它只是观察流中的元素状态。- 主要用于调试和日志记录,不应在生产代码中滥用它来执行关键逻辑。
peek()的操作是中间操作,它不会触发流的处理,流的处理会在终端操作(如forEach())时完成。
总结
peek()是一个中间操作,用于在流操作过程中查看每个元素的状态。- 适用于调试和记录日志,但不应在流中进行实际的元素修改。
- 它允许在流的处理中插入调试信息而不改变流的内容。
mapToDouble().sum()
语法
CodeDoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
语法
Codedouble sum()
假设有多个账户金额,想拿到总的金额:
double totalAmount = orders.stream()
.mapToDouble(Order::getAmount)
.sum();
总结
mapToDouble()用于将流中的元素映射为double类型的流。sum()用于计算DoubleStream中所有double值的总和。- 这两个方法的组合非常适用于需要计算数值数据总和的场景。