当前位置: 首页 > news >正文

函数式编程(四)Stream流使用

一、概述

在使用stream之前,先理解Optional

OptionalJava 8引入的一个容器类,用于处理可能为空的值。它提供了一种优雅的方式来处理可能存在或不存在的值,避免了空指针异常。

Optional的主要特点如下:

  • 可能为空:Optional可以包含一个非空的值,也可以表示为空。
  • 避免空指针异常:通过使用Optional,可以避免在访问可能为空的值时出现空指针异常。
  • 显式判断:使用Optional需要显式地判断值是否存在,以便进行相应的处理。
  • 函数式操作:Optional提供了一系列的函数式操作方法,如map()filter()orElse()等,方便对Optional中的值进行转换、过滤和默认值处理。

使用Stream可以对集合或数组中的元素进行各种转换、过滤和映射等操作,以实现更简洁、灵活和函数式的编程风格。下面是使用Stream的一般步骤:

  • 创建Stream:通过集合、数组、IO通道或Stream的静态方法来创建一个Stream对象。
  • 中间操作(Intermediate Operations):使用中间操作方法对Stream进行转换、过滤、映射等操作,返回一个新的Stream对象。常见的中间操作包括filter()map()sorted()distinct()等。
  • 终止操作(Terminal Operations):使用终止操作方法对Stream进行最终的计算或收集操作,返回一个结果或一个最终的集合。常见的终止操作包括forEach()collect()reduce()min()max()等。

二、Stream的创建

在Java中,可以使用多种方式来创建Stream对象。下面是一些常见的创建Stream的方法:

1. 通过集合创建Stream

通过集合创建Stream是一种常见的方式,Java8 中的 Collection 接口被扩展,提供两个获取流的方法 :

  • Stream stream() : 返回一个顺序流
  • Stream parallelStream() : 返回一个并行流
Stream<Integer> stream1 = Arrays.asList(1,2,3,4).stream();
Stream<Integer> stream2 = Arrays.asList(1,2,3,4).parallelStream();

2. 通过数组创建Stream

通过数组创建Stream可以使用Arrays.stream()方法来实现。该方法接受一个数组作为参数,并返回一个对应类型的Stream对象。

int[] array = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(array);

3. 通过Stream的静态方法创建Stream

  • 通过Stream的静态方法,可以使用of()iterate()generate()等方法来创建Stream对象。
  • 使用IntStreamLongStreamDoubleStreamstatic方法创建有限流,可以使用of()range()rangeClosed()等方法来创建Stream对象。
  • 使用随机数类的ints()方法创建无限数值流
// 使用of()方法创建Stream
Stream<String> stream1 = Stream.of("apple", "banana", "orange");
// 使用iterate()方法创建Stream
Stream<Integer> stream2 = Stream.iterate(0, n -> n + 2).limit(10);
// 使用generate()方法创建Stream
Stream<Double> stream3 = Stream.generate(Math::random).limit(5);// 使用of()方法创建IntStream
IntStream.of(new int[]{1, 2, 3});
// 使用range()方法创建IntStream
IntStream.range(1, 3);
// 使用rangeClosed()方法创建IntStream
IntStream.rangeClosed(1, 3);// 使用随机数类的ints()方法创建无限数值流
Random random = new Random();
IntStream ints = random.ints();

4. 通过IO通道创建Stream

  • 使用BufferedReaderlines方法从文件中获得行的流
  • Files类的操作路径的方法,如listfindwalklines
// 使用BufferedReader的lines方法从文件中获得行的流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("file.txt")));
Stream<String> lines = bufferedReader.lines();Path path = Paths.get("file.txt");
Stream<String> lines = Files.lines(path);

5. 通过其他类提供的创建Stream

  • BitSet数值流
  • Pattern 将字符串分隔成流
  • JarFile 读取jar文件流
// BitSet数值流
IntStream stream = new BitSet().stream();// Pattern 将字符串分隔成流
Pattern pattern = compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);// JarFile 读取jar文件流
Stream<JarEntry> stream = new JarFile("").stream();

这些方法提供了不同的方式来创建Stream对象,可以根据具体的需求选择适当的创建方式。创建Stream后,可以使用Stream的中间操作和终止操作来对数据进行处理和操作。

需要注意的是,Stream对象是一次性使用的,一旦对Stream进行了终止操作,就不能再对同一个Stream进行其他操作。如果需要对同一组数据进行多个操作,可以创建多个Stream对象来实现。

三、中间操作

3.1 筛选(filter

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

filter()是Java Stream API中的一个中间操作方法,用于根据指定的条件过滤流中的元素。它接受一个Predicate函数式接口作为参数,该接口定义了一个用于判断元素是否满足条件的方法。

filter()方法的语法:Stream<T> filter(Predicate<? super T> predicate)

其中,T表示流中的元素类型,predicate表示用于判断元素是否满足条件的Predicate对象。

filter()方法的工作原理如下:

  • 对于流中的每个元素,filter()方法会调用传入的Predicate对象的test()方法,将当前元素作为参数传递给test()方法。
  • 如果test()方法返回true,则表示当前元素满足条件,会被保留在新的流中。
  • 如果test()方法返回false,则表示当前元素不满足条件,会被过滤掉,不包含在新的流中。

下面是一个示例代码,演示了如何使用filter()方法过滤出偶数:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamFilterExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 使用filter()方法过滤出偶数List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());// 输出结果System.out.println(evenNumbers); // [2, 4, 6, 8, 10]}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用filter()方法过滤出偶数,通过传入的Lambda表达式判断元素是否为偶数。最后,我们使用collect()方法将过滤后的结果收集到一个新的列表中,并输出结果。

通过使用filter()方法,我们可以根据指定的条件过滤流中的元素,只保留满足条件的元素,实现对数据的筛选和过滤操作。

3.2 元素转换(peek/map/flatMap)

在Java的Stream API中,peek()map()flatMap()是常用的中间操作方法,用于对流中的元素进行转换、处理和操作。

peek()方法:

  • peek()方法接受一个Consumer函数式接口作为参数,对流中的每个元素执行指定的操作,并返回一个新的流。
  • peek()方法可以用于调试和观察流中的元素,但不会改变流中元素的内容。
  • peek()方法是一个中间操作,它返回的是与原始流相同类型的新流。

map()方法:

  • map()方法接受一个Function函数式接口作为参数,将流中的每个元素映射为另一个元素,并返回一个新的流。
  • map()方法可以用于对流中的元素进行转换、提取或计算等操作。
  • map()方法是一个中间操作,它返回的是与原始流中元素类型不同的新流。

flatMap()方法:

  • flatMap()方法接受一个Function函数式接口作为参数,将流中的每个元素映射为一个流,并将所有流连接成一个新的流。
  • flatMap()方法可以用于扁平化嵌套的流结构,将多个流合并为一个流。
  • flatMap()方法是一个中间操作,它返回的是与原始流中元素类型不同的新流。

下面是一个示例代码,演示了peek()、map()和flatMap()的使用:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamOperationsExample {public static void main(String[] args) {List<String> words = Arrays.asList("Hello", "World", "Java");// 使用peek()方法调试和观察流中的元素List<String> peekResult = words.stream().peek(System.out::println).collect(Collectors.toList());// 使用map()方法将每个单词转换为大写List<String> mapResult = words.stream().map(String::toUpperCase).collect(Collectors.toList());// 使用flatMap()方法将每个单词拆分为字母List<String> flatMapResult = words.stream().flatMap(word -> Arrays.stream(word.split(""))).collect(Collectors.toList());// 输出结果System.out.println("Peek Result: " + peekResult);System.out.println("Map Result: " + mapResult);System.out.println("FlatMap Result: " + flatMapResult);}
}

在上面的示例中,我们首先创建了一个包含单词的列表。然后,我们使用peek()方法对流中的元素进行调试和观察,并使用map()方法将每个单词转换为大写,最后使用flatMap()方法将每个单词拆分为字母。最终,我们使用collect()方法将结果收集到一个新的列表中,并输出结果。

通过使用peek()map()flatMap()等方法,我们可以对流中的元素进行转换、处理和操作,实现更加灵活和函数式的编程风格。

3.3 排序(sorted)

sorted()是Java Stream API中的一个中间操作方法,用于对流中的元素进行排序。它可以按照自然顺序或者通过自定义的Comparator来进行排序。

sorted()方法的语法:Stream<T> sorted() / Stream<T> sorted(Comparator<? super T> comparator)

其中,T表示流中的元素类型,comparator表示用于比较元素的Comparator对象。

sorted()方法的工作原理如下:

  • 对于无参的sorted()方法,它会使用元素的自然顺序进行排序。元素类型必须实现Comparable接口,否则会抛出ClassCastException
  • 对于带有Comparator参数的sorted()方法,它会使用指定的Comparator对象来进行排序。Comparator定义了元素之间的比较规则。

下面是一个示例代码,演示了如何使用sorted()方法对整数列表进行排序:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamSortedExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);// 使用sorted()方法对整数列表进行排序List<Integer> sortedNumbers = numbers.stream().sorted().collect(Collectors.toList());// 输出结果System.out.println(sortedNumbers); // [1, 2, 3, 5, 8, 9]}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用sorted()方法对整数列表进行排序,默认按照自然顺序进行排序。最后,我们使用collect()方法将排序后的结果收集到一个新的列表中,并输出结果。

通过使用sorted()方法,我们可以对流中的元素进行排序,使得元素按照指定的顺序排列。可以根据需要使用自然顺序或自定义的Comparator来进行排序操作。

3.4 截取/跳过(limit/skip)

limit()skip()是Java Stream API中的两个中间操作方法,用于限制流中元素的数量。

limit()方法:

  • limit()方法接受一个long类型的参数,用于限制流中元素的数量。
  • 它返回一个新的流,其中包含原始流中的前n个元素(如果原始流中的元素不足n个,则返回所有元素)。

skip()方法:

  • skip()方法接受一个long类型的参数,用于跳过流中的前n个元素。
  • 它返回一个新的流,其中包含原始流中剩余的元素(如果原始流中的元素少于n个,则返回空流)。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamLimitSkipExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用limit()方法限制流中元素的数量List<Integer> limitedNumbers = numbers.stream().limit(3).collect(Collectors.toList());// 使用skip()方法跳过流中的前N个元素List<Integer> skippedNumbers = numbers.stream().skip(2).collect(Collectors.toList());// 输出结果System.out.println(limitedNumbers); // [1, 2, 3]System.out.println(skippedNumbers); // [3, 4, 5]}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用limit()方法限制流中元素的数量为3个,通过collect()方法将结果收集到一个新的列表中。接着,我们使用skip()方法跳过流中的前2个元素,同样通过collect()方法将结果收集到另一个新的列表中。最后,我们输出限制和跳过操作后的结果。

通过使用limit()skip()方法,我们可以对流中的元素进行数量的限制和跳过操作,实现对数据的筛选和截取。

3.5 合并(concat)

concat()是Java Stream API中的一个静态方法,用于将两个流连接起来形成一个新的流。它接受两个相同类型的流作为参数,并返回一个包含两个流中所有元素的新流。

concat()方法的语法:static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

其中,T表示流中的元素类型,ab表示要连接的两个流。

concat()方法的工作原理如下:

  • 它会将第一个流的所有元素放在新流中,然后将第二个流的所有元素追加到新流的末尾。
  • 新流中的元素顺序与原始流的顺序保持一致。

下面是一个示例代码,演示了如何使用concat()方法连接两个流:

import java.util.stream.Stream;public class StreamConcatExample {public static void main(String[] args) {Stream<Integer> stream1 = Stream.of(1, 2, 3);Stream<Integer> stream2 = Stream.of(4, 5, 6);// 使用concat()方法连接两个流Stream<Integer> concatenatedStream = Stream.concat(stream1, stream2);// 输出结果concatenatedStream.forEach(System.out::println); // 1, 2, 3, 4, 5, 6}
}

在上面的示例中,我们首先创建了两个整数流。然后,我们使用concat()方法将这两个流连接起来,形成一个新的流。最后,我们使用forEach()方法遍历并打印连接后的结果。

通过使用concat()方法,我们可以将两个流连接起来,形成一个包含两个流中所有元素的新流。这对于需要合并多个流的场景非常有用。

3.6 去重(distinct)

distinct()是Java Stream API中的一个中间操作方法,用于去除流中的重复元素。它会返回一个新的流,其中包含原始流中的所有不重复的元素。

distinct()方法使用元素的equals()方法来判断元素是否重复。如果两个元素相等,则只保留第一个出现的元素,后续出现的相同元素将被过滤掉。

distinct()方法的语法:Stream<T> distinct()

其中,T表示流中的元素类型。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamDistinctExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 4, 3, 5);// 使用distinct()方法去除重复元素List<Integer> distinctNumbers = numbers.stream().distinct().collect(Collectors.toList());// 输出结果System.out.println(distinctNumbers); // [1, 2, 3, 4, 5]}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用distinct()方法去除列表中的重复元素,并通过collect()方法将结果收集到一个新的列表中。最后,我们输出去除重复元素后的结果。

通过使用distinct()方法,我们可以方便地去除流中的重复元素,得到一个只包含不重复元素的新流。这对于数据去重和筛选操作非常有用。

四、终止操作

4.1 遍历/匹配(foreach/findAny/findFirst/anyMatch/allMatch/noneMatch

在Java Stream API中,forEach()findAny()findFirst()anyMatch()allMatch()noneMatch()是常用的终止操作方法,用于对流中的元素进行遍历、查找和匹配。

forEach()方法:

  • forEach()方法接受一个Consumer函数式接口作为参数,对流中的每个元素执行指定的操作。
  • 它没有返回值,只是对流中的每个元素进行操作。

findAny()findFirst()方法:

  • findAny()方法返回流中的任意一个元素,如果流为空则返回Optional.empty()
  • findFirst()方法返回流中的第一个元素,如果流为空则返回Optional.empty()

anyMatch()allMatch()noneMatch()方法:

  • anyMatch()方法接受一个Predicate函数式接口作为参数,判断流中是否存在满足指定条件的元素。
  • allMatch()方法接受一个Predicate函数式接口作为参数,判断流中的所有元素是否都满足指定条件。
  • noneMatch()方法接受一个Predicate函数式接口作为参数,判断流中是否没有任何元素满足指定条件。
  • 这些方法返回一个boolean类型的结果,表示是否存在满足条件的元素。
import java.util.Arrays;
import java.util.List;public class StreamTerminalOperationsExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用forEach()方法遍历每个元素numbers.stream().forEach(System.out::println);// 使用findAny()方法找到任意一个元素Integer anyNumber = numbers.stream().findAny().orElse(null);// 使用findFirst()方法找到第一个元素Integer firstNumber = numbers.stream().findFirst().orElse(null);// 使用anyMatch()方法判断是否存在大于3的元素boolean anyGreaterThanThree = numbers.stream().anyMatch(n -> n > 3);// 使用allMatch()方法判断是否所有元素都小于10boolean allLessThanTen = numbers.stream().allMatch(n -> n < 10);// 使用noneMatch()方法判断是否没有任何元素等于6boolean noneEqualsSix = numbers.stream().noneMatch(n -> n == 6);// 输出结果System.out.println("Any Number: " + anyNumber);System.out.println("First Number: " + firstNumber);System.out.println("Any Greater Than Three: " + anyGreaterThanThree);System.out.println("All Less Than Ten: " + allLessThanTen);System.out.println("None Equals Six: " + noneEqualsSix);}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用不同的终止操作方法对流中的元素进行遍历、查找和匹配操作,并输出结果。

通过使用这些终止操作方法,我们可以对流中的元素进行遍历、查找和匹配操作,得到相应的结果。

4.2 聚合(max/min/count)

在Java Stream API中,max()min()count()是常用的终止操作方法,用于获取流中的最大值、最小值和元素数量。

max()方法:

  • max()方法接受一个Comparator函数式接口作为参数,用于比较流中的元素。
  • 它返回流中的最大元素,如果流为空则返回Optional.empty()

min()方法:

  • min()方法接受一个Comparator函数式接口作为参数,用于比较流中的元素。
  • 它返回流中的最小元素,如果流为空则返回Optional.empty()

count()方法:

  • count()方法返回流中的元素数量,返回一个long类型的结果。
import java.util.Arrays;
import java.util.List;
import java.util.Optional;public class StreamTerminalOperationsExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用max()方法找到最大值Optional<Integer> maxNumber = numbers.stream().max(Integer::compare);// 使用min()方法找到最小值Optional<Integer> minNumber = numbers.stream().min(Integer::compare);// 使用count()方法计算元素数量long count = numbers.stream().count();// 输出结果System.out.println("Max Number: " + maxNumber.orElse(null));System.out.println("Min Number: " + minNumber.orElse(null));System.out.println("Count: " + count);}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用不同的终止操作方法对流中的元素进行最大值、最小值和计数操作,并输出结果。

通过使用这些终止操作方法,我们可以方便地获取流中的最大值、最小值和元素数量。需要注意的是,max()min()方法返回的是Optional类型的结果,因为流中可能为空。

4.3 归约(reduce)

reduce()是Java Stream API中的一个终止操作方法,用于将流中的元素按照指定的规约操作进行合并,返回一个最终的结果。归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

reduce()方法接受一个BinaryOperator函数式接口作为参数,该接口定义了一个二元操作,用于将两个元素进行合并。它还可以接受一个初始值作为累加器的初始值。

reduce()方法的语法:

Optional<T> reduce(BinaryOperator<T> accumulator)

T reduce(T identity, BinaryOperator<T> accumulator)

其中,T表示流中的元素类型,accumulator表示用于合并元素的操作,identity表示累加器的初始值。

reduce()方法的工作原理如下:

  • 对于流中的第一个元素,将其作为累加器的初始值。
  • 对于后续的每个元素,使用累加器和当前元素进行合并操作,得到一个新的累加器值。
  • 最终返回合并后的累加器值。
import java.util.Arrays;
import java.util.List;
import java.util.Optional;public class StreamReduceExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用reduce()方法对整数列表进行求和Optional<Integer> sum = numbers.stream().reduce(Integer::sum);// 输出结果System.out.println("Sum: " + sum.orElse(0));}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用reduce()方法对整数列表进行求和,通过传入的Integer::sum方法作为累加器进行合并操作。最后,我们使用orElse()方法获取求和结果,并输出结果。

通过使用reduce()方法,我们可以对流中的元素进行合并操作,实现对数据的聚合和规约。需要注意的是,reduce()方法返回的是Optional类型的结果,因为流中可能为空。

4.4 收集(collect)

collect,收集,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。

collect()用于将流中的元素收集到一个集合或其他数据结构中。

collect()方法接受一个Collector对象作为参数,该对象定义了如何将流中的元素进行收集和组合。Collector接口提供了一系列静态方法来创建常见的收集器实例。

下面是collect()方法的语法:<R> R collect(Collector<? super T, A, R> collector)

其中,T表示流中的元素类型,A表示中间结果的类型,R表示最终结果的类型。

collect()方法的工作原理如下:

  • 它会使用Collector对象中定义的逻辑,对流中的元素进行收集和组合。
  • 最终返回一个包含收集结果的对象,可以是ListSetMap等。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamCollectExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用collect()方法将偶数收集到一个新的列表中List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());// 输出结果System.out.println(evenNumbers); // [2, 4]}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用filter()方法过滤出偶数,并通过collect()方法将结果收集到一个新的列表中。最后,我们输出收集后的结果。

通过使用collect()方法,我们可以方便地将流中的元素收集到一个集合或其他数据结构中,实现对数据的聚合和收集操作。

4.4.1 归集(toList/toSet/toMap)

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toListtoSettoMap比较常用,另外还有toCollectiontoConcurrentMap等复杂一些的用法。

在Java Stream API中,有多个方法可用于将流中的元素收集到不同类型的集合或映射中。

  1. toList()方法:
    • toList()方法将流中的元素收集到一个列表中。
    • 它返回一个包含流中所有元素的新列表。
  1. toSet()方法:
    • toSet()方法将流中的元素收集到一个集合中。
    • 它返回一个包含流中所有元素的新集合。
  1. toMap()方法:
    • toMap()方法将流中的元素收集到一个映射中。
    • 它接受两个Function函数式接口作为参数,用于提取键和值,并返回一个包含流中所有键值对的新映射。
  1. toCollection()方法:
    • toCollection()方法将流中的元素收集到指定类型的集合中。
    • 它接受一个Supplier函数式接口作为参数,用于提供一个自定义的集合实例。
  1. toConcurrentMap()方法:
    • toConcurrentMap()方法将流中的元素收集到一个并发映射中。
    • 它接受三个Function函数式接口作为参数,用于提取键、值和处理键冲突的方式。

这些方法都是终止操作,它们根据需要将流中的元素收集到不同类型的集合或映射中。根据具体的需求和数据结构,可以选择适当的方法来进行元素的收集和聚合操作。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;public class StreamCollectExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用toList()方法将元素收集到列表中List<Integer> list = numbers.stream().collect(Collectors.toList());// 使用toSet()方法将元素收集到集合中Set<Integer> set = numbers.stream().collect(Collectors.toSet());// 使用toMap()方法将元素收集到映射中Map<Integer, String> map = numbers.stream().collect(Collectors.toMap(num -> num,num -> "Value" + num));// 使用toCollection()方法将元素收集到自定义集合中Set<Integer> customSet = numbers.stream().collect(Collectors.toCollection(() -> new CustomSet<>()));// 使用toConcurrentMap()方法将元素收集到并发映射中Map<Integer, String> concurrentMap = numbers.stream().collect(Collectors.toConcurrentMap(num -> num,num -> "Value" + num,(v1, v2) -> v1 + ", " + v2,ConcurrentHashMap::new));// 输出结果System.out.println("List: " + list); // [1, 2, 3, 4, 5]System.out.println("Set: " + set); // [1, 2, 3, 4, 5]System.out.println("Map: " + map); // {1=Value1, 2=Value2, 3=Value3, 4=Value4, 5=Value5}System.out.println("Custom Set: " + customSet);System.out.println("Concurrent Map: " + concurrentMap);}
}class CustomSet<T> extends HashSet<T> {// 自定义集合类
}

通过使用toList()toSet()toMap()等方法,我们可以方便地将流中的元素收集到列表、集合或映射中,实现对数据的聚合和收集操作。

通过使用toCollection()toConcurrentMap()等方法,我们可以方便地将流中的元素收集到自定义的集合或并发映射中,实现对数据的聚合和收集操作。

4.4.2 统计(count/averaging/max(min)/summing/summarizing)

在Java Stream API中,有多个终止操作方法可以用于对流中的元素进行计数、平均值、最大值、最小值、求和和统计等操作。

  1. count()方法:
    • count()方法返回流中的元素数量。
    • 它返回一个long类型的结果,表示流中的元素数量。
  1. averagingXxx()方法:
    • averagingXxx()方法用于计算流中元素的平均值,其中Xxx可以是IntLongDouble
    • 它返回一个double类型的结果,表示流中元素的平均值。
  1. maxBy()minBy()方法:
    • maxBy()方法接受一个Comparator函数式接口作为参数,返回流中的最大元素。
    • minBy()方法接受一个Comparator函数式接口作为参数,返回流中的最小元素。
    • 它们返回一个Optional对象,表示流中的最大或最小元素。
  1. summingXxx()方法:
    • summingXxx()方法用于对流中的元素进行求和,其中Xxx可以是IntLongDouble
    • 它返回一个Xxx类型的结果,表示流中元素的总和。
  1. summarizingXxx()方法:
    • summarizingXxx()方法用于对流中的元素进行统计,其中Xxx可以是IntLongDouble
    • 它返回一个包含元素数量、总和、平均值、最大值和最小值的XxxSummaryStatistics对象。
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;public class StreamStatisticsExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用count()方法统计元素数量long count = numbers.stream().count();// 使用averagingXxx()方法计算平均值double average = numbers.stream().collect(Collectors.averagingInt(Integer::intValue));// 使用maxBy()方法找到最大值Optional<Integer> max = numbers.stream().max(Integer::compare);// 使用minBy()方法找到最小值Optional<Integer> min = numbers.stream().min(Integer::compare);// 使用summingXxx()方法求和int sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue));// 使用summarizingXxx()方法进行统计IntSummaryStatistics statistics = numbers.stream().collect(Collectors.summarizingInt(Integer::intValue));// 输出结果System.out.println("Count: " + count);System.out.println("Average: " + average);System.out.println("Max: " + max.orElse(null));System.out.println("Min: " + min.orElse(null));System.out.println("Sum: " + sum);System.out.println("Statistics: " + statistics);}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用不同的终止操作方法对流中的元素进行计数、平均值、最大值、最小值、求和和统计操作,并输出结果。

通过使用这些终止操作方法,我们可以方便地对流中的元素进行各种统计操作,得到相应的结果。

4.4.3 分组(partitioningBy/groupingBy)

在Java Stream API中,partitioningBy()groupingBy()是用于对流中的元素进行分区和分组的收集器。

partitioningBy()方法:

  • partitioningBy()方法接受一个Predicate函数式接口作为参数,用于将流中的元素分为满足条件和不满足条件的两个部分。
  • 它返回一个Map<Boolean, List<T>>类型的结果,其中Boolean表示分区的键,List<T>表示满足或不满足条件的元素列表。

groupingBy()方法:

  • groupingBy()方法接受一个Function函数式接口作为参数,用于根据指定的分类函数对流中的元素进行分组。
  • 它返回一个Map<K, List<T>>类型的结果,其中K表示分组的键,List<T>表示具有相同键的元素列表。
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class StreamPartitioningGroupingExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);// 使用partitioningBy()方法将奇偶数分区Map<Boolean, List<Integer>> partitionedNumbers = numbers.stream().collect(Collectors.partitioningBy(n -> n % 2 == 0));// 使用groupingBy()方法将数字按照奇偶分组Map<String, List<Integer>> groupedNumbers = numbers.stream().collect(Collectors.groupingBy(n -> n % 2 == 0 ? "Even" : "Odd"));// 输出结果System.out.println("Partitioned Numbers: " + partitionedNumbers);System.out.println("Grouped Numbers: " + groupedNumbers);}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用partitioningBy()方法将列表中的元素按照奇偶数进行分区,使用groupingBy()方法将列表中的元素按照奇偶进行分组。最后,我们输出分区和分组的结果。

通过使用partitioningBy()groupingBy()方法,我们可以方便地对流中的元素进行分区和分组操作,实现对数据的分类和归类。

4.4.4 接合(joining)

在Java Stream API中,joining()是一个终止操作方法,用于将流中的元素连接成一个字符串。

joining()方法没有参数,它返回一个包含流中所有元素连接后的字符串。默认情况下,元素之间使用空字符串作为分隔符进行连接,但也可以通过提供自定义的分隔符来指定连接时使用的分隔符。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamJoiningExample {public static void main(String[] args) {List<String> words = Arrays.asList("Hello", "World", "Java");// 使用joining()方法将字符串列表中的元素连接成一个字符串String result = words.stream().collect(Collectors.joining());// 输出结果System.out.println(result); // HelloWorldJava// 使用自定义分隔符连接字符串列表中的元素String customResult = words.stream().collect(Collectors.joining(", "));// 输出结果System.out.println(customResult); // Hello, World, Java}
}

在上面的示例中,我们首先创建了一个包含字符串的列表。然后,我们使用joining()方法将列表中的元素连接成一个字符串,默认使用空字符串作为分隔符。接着,我们使用自定义的分隔符" , "来连接字符串列表中的元素。最后,我们输出连接后的结果。

通过使用joining()方法,我们可以方便地将流中的元素连接成一个字符串,实现对数据的合并和拼接操作。

4.4.5 归约(reducing)

在Java Stream API中,reducing()是一个终止操作方法,用于将流中的元素按照指定的规约操作进行合并,返回一个最终的结果。

reducing()方法接受一个初始值和一个BinaryOperator函数式接口作为参数,该接口定义了一个二元操作,用于将两个元素进行合并。它还可以接受一个Function函数式接口,用于将流中的元素转换为另一种类型。

下面是reducing()方法的语法:

Optional<T> reducing(BinaryOperator<T> accumulator)

T reducing(T identity, BinaryOperator<T> accumulator)

<U> U reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> accumulator)

其中,T表示流中的元素类型,U表示结果的类型,accumulator表示用于合并元素的操作,identity表示初始值,mapper表示元素转换的函数。

reducing()方法的工作原理如下:

  • 对于流中的第一个元素,将其作为累加器的初始值。
  • 对于后续的每个元素,使用累加器和当前元素进行合并操作,得到一个新的累加器值。
  • 最终返回合并后的累加器值。
import java.util.Arrays;
import java.util.List;
import java.util.Optional;public class StreamReducingExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用reducing()方法对整数列表进行求和Optional<Integer> sum = numbers.stream().reduce(Integer::sum);// 输出结果System.out.println("Sum: " + sum.orElse(0));}
}

在上面的示例中,我们首先创建了一个包含整数的列表。然后,我们使用reduce()方法对整数列表进行求和,通过传入的Integer::sum方法作为累加器进行合并操作。最后,我们使用orElse()方法获取求和结果,并输出结果。

通过使用reducing()方法,我们可以对流中的元素进行合并操作,实现对数据的聚合和规约。需要注意的是,reduce()方法返回的是Optional类型的结果,因为流中可能为空。

相关文章:

函数式编程(四)Stream流使用

一、概述 在使用stream之前&#xff0c;先理解Optional 。 Optional是Java 8引入的一个容器类&#xff0c;用于处理可能为空的值。它提供了一种优雅的方式来处理可能存在或不存在的值&#xff0c;避免了空指针异常。 Optional的主要特点如下&#xff1a; 可能为空&#xff…...

区块链面临六大安全问题 安全测试方案研究迫在眉睫

区块链面临六大安全问题 安全测试方案研究迫在眉睫 近年来&#xff0c;区块链技术逐渐成为热门话题&#xff0c;其应用前景受到各国政府、科研机构和企业公司的高度重视与广泛关注。随着技术的发展&#xff0c;区块链应用与项目层出不穷&#xff0c;但其安全问题不容忽视。近年…...

K8S---kubelet TLS 启动引导

一、引导启动初始化过程(Bootstrap Initialization ) 1、kubeadm 生成一个Token,类似07401b.f395accd246ae52d这种格式,或者自己手动生成2、使用kubectl命令行,生成一个Secret,具体详见认证、授权3、kubelet 进程启动 (begin)4、kubelet 看到自己没有对应的 kubeconfig…...

Android系统修改驱动固定USB摄像头节点绑定前后置摄像头

前言 Android系统中usb摄像头节点会因为摄像头所接的usb口不同或者usb设备识别顺序不一样而出现每次开机生成的video节点不一样的问题。由于客户app调用摄像头时,需要固定摄像头的节点。因此需要针对前面的情况做处理。 方式1:通过摄像头名称固定摄像头节点 --- a/kernel…...

RT-Thread 内核移植

内核移植 内核移植就是将RTT内核在不同的芯片架构、不同的板卡上运行起来&#xff0c;能够具备线程管理和调度&#xff0c;内存管理&#xff0c;线程间同步等功能。 移植可分为CPU架构移植和BSP&#xff08;Board support package&#xff0c;板级支持包&#xff09;移植两部…...

springboot中entity层、dto层、vo层通俗理解三者的区别

entity&#xff1a;这个类的属性是跟数据库字段一模一样的&#xff08;驼峰命名&#xff09;&#xff0c;当我们使用MyBatis-Plus的时候经常用得到。 dto&#xff1a;用于后端接收前端返回的数据&#xff0c;一般是post请求&#xff0c;前端会给我们返回一个json对象&#xff…...

TypeScript_队列结构-链表

队列 队列&#xff08;Queue&#xff09;&#xff0c;它是一种受限的线性表&#xff0c;先进先出&#xff08;FIFO First In First Out&#xff09; 受限之处在于它只允许在队列的前端&#xff08;front&#xff09;进行删除操作而在队列的后端&#xff08;rear&#xff09;进…...

STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库

通过DMA方式输出PWM模拟LED数据信号 优点&#xff1a;不消耗CPU资源 缺点&#xff1a;占用内存较大 STM32CUBEMX配置 定时器配置 定时器通道&#xff1a;TIM3 CH2 分频&#xff1a;0 重装值&#xff1a;79&#xff0c;芯片主频64Mhz&#xff0c;因此PWM输出频率&#xff1a…...

记录错误:Access denied for user ‘root‘@‘localhost‘ (using password:No) 解决方案

他说我没输入密码&#xff0c;但是我输入了啊&#xff1f;&#xff1f;于是&#xff0c;我试了试这儿&#xff0c;password 一改就好了。。。 他原来是是我打的很快&#xff0c;快速生成的。。。。...

python爬虫实战(5)--获取小破站热榜

1. 分析地址 打开小破站热榜首页&#xff0c;查看响应找到如下接口地址 2. 编码 定义请求头 拿到标头 复制粘贴&#xff0c;处理成json 处理请求头代码如下: def format_headers_to_json():f open("data.txt", "r", encoding"utf-8") # 读…...

单目标应用:基于麻雀搜索算法SSA的微电网优化调度MATLAB

一、微网系统运行优化模型 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、麻雀搜索算法简介 麻雀搜索算法 (Sparrow Search Algorithm, SSA) 是一种新型的群智能优化算法&#xff0c;于2020…...

C# easymodbus

库介绍 EasyModbus是用于 .NET 和 Java 平台上的Modbus TCP/UDP/RTU通讯协议库&#xff0c;支持多种编程语言&#xff0c;如C#、VB.NET、Java、C 与更多C#的变体&#xff0c;如Unity、Mono、.NET Core等等。 EasyModbus的Java版本至少需要Java 7&#xff0c;而C#版本兼容 .NE…...

HikariCP源码修改,使其连接池支持Kerberos认证

HikariCP-4.0.3 修改HikariCP源码,使其连接池支持Kerberos认证 修改后的Hikari源码地址:https://github.com/Raray-chuan/HikariCP-4.0.3 Springboot使用hikari连接池并进行Kerberos认证访问Impala的demo地址:https://github.com/Raray-chuan/springboot-kerberos-hikari-im…...

5分钟看明白rust mod use

rust把mod简单的事没说清&#xff0c;一片混乱&#xff0c;似懂非懂. mod语句查找只有一条规则&#xff1a;先找mod名1.rs,没有就我同名文件夹下的mod名1.rs&#xff0c;如果没有&#xff0c;就同名文件夹下的mod名1/mod.rs,再没有就error. 在mod.rs中&#xff0c;pub mod 文件…...

【Java核心知识】ThreadLocal相关知识

ThreadLocal 什么是ThreadLocal ThreadLoacal类可以为每个线程保存一份独有的变量&#xff0c;该变量对于每个线程都是独占的。实现原理为每个Thread类中包含一个ThreadHashMap&#xff0c;key为变量的对应的ThreadLocal对象&#xff0c;value为变量的值。 在日常使用中&…...

《Python基础教程(第三版)》阅读笔记 1

目录 1 快速上手&#xff1a;基础知识2 列表和元组3 字符串4 字典5 条件、循环及其他6 抽象7 再谈抽象8 异常9 魔法方法、特性和迭代器10 开箱即用 本文参考自《Beginning Python: from novice to professional》&#xff0c;中文版为《Python基础教程&#xff08;第三版&#…...

坦克400 Hi4-T预售价28.5万元起,越野新能源好理解

8月25日&#xff0c;在以“智享蓉城&#xff0c;驭见未来”为主题的成都国际车展上&#xff0c;坦克品牌越野新能源再启新程&#xff0c;首次以全Hi4-T新能源阵容亮相展台&#xff0c;释放坦克品牌加速布局越野新能源的强烈信号。 Hi4-T架构首款落地车型坦克500 Hi4-T上市至今斩…...

我的Vim学习笔记(不定期更新)

2023年9月3日&#xff0c;周日上午 学到了啥就写啥&#xff0c;不定期更新 目录 字体 文件 标签页 分屏 调用系统命令 字体 设置字体大小 :set guifont字体:h字体大小 例如&#xff0c;:set guifontMonospace:h20 查询当前使用的字体和字体大小 :set guifont? 查看…...

spring boot项目生成容器并运行

一个安静的周末&#xff0c;shigen又睡懒觉了&#xff0c;上次说的拖延症的惩罚来了&#xff1a;早晚各100个健腹轮练习&#xff0c;早上的已经完成了。今天的文章来的有点晚&#xff0c;但是依旧保持质量。 springboot项目生成容器并运行 背景 将springboot项目打包成jar包&…...

Vue之html中特殊符号的展示

Vue之html中特殊符号的展示 在html中使用特殊字符时直接展示会报错&#xff0c;需要使用实体名称或者实体编号才能展示。 最常用的字符实体 显示结果 描述 实体名称 实体编号空格 < 小于号 < &…...

数据结构1 -- leetcode练习

三. 练习 3.1 时间复杂度 用函数 f ( n ) f(n) f(n) 表示算法效率与数据规模的关系&#xff0c;假设每次解决问题需要 1 微秒&#xff08; 1 0 − 6 10^{-6} 10−6 秒&#xff09;&#xff0c;进行估算&#xff1a; 如果 f ( n ) n 2 f(n) n^2 f(n)n2 那么 1 秒能解决多…...

Java设计模式:四、行为型模式-05:备忘录模式

文章目录 一、定义&#xff1a;备忘录模式二、模拟场景&#xff1a;备忘录模式三、改善代码&#xff1a;备忘录模式3.1 工程结构3.2 备忘录模式模型结构图3.3 备忘录模式定义3.3.1 配置信息类3.3.2 备忘录类3.3.3 记录者类3.3.4 管理员类 3.4 单元测试 四、总结&#xff1a;备忘…...

MongoDB实验——MongoDB配置用户的访问控制

MongoDB 配置用户的访问控制 一、 实验原理 理解admin数据库&#xff1a;安装MongoDB时&#xff0c;会自动创建admin数据库&#xff0c;这是一个特殊数据库&#xff0c;提供了普通数据库没有的功能&#xff0c;例如&#xff0c;有些账户角色赋予用户操作多个数据库的权限&…...

golang逃逸技术分析

“ 申请到栈内存好处&#xff1a;函数返回直接释放&#xff0c;不会引起垃圾回收&#xff0c;对性能没有影响。 申请到堆上面的内存才会引起垃圾回收。 func F() { a : make([]int, 0, 20) b : make([]int, 0, 20000) l : 20 c : make([]int, 0, l)} “ a和b代码一样&#xff0…...

说说你了解的 Nginx

分析&回答 nginx性能数据 高并发连接: 官方称单节点支持5万并发连接数&#xff0c;实际生产环境能够承受2-3万并发。内存消耗少: 在3万并发连接下&#xff0c;开启10个nginx进程仅消耗150M内存 (15M10150M) 1. 正向、反向代理 所谓“代理”&#xff0c;是指在内网边缘 …...

SpringWeb(SpringMVC)

目录 SpringWeb介绍 搭建 SpringWeb SpringWeb介绍 Spring Web是一个基于 Servlet API 构建的原始 web 框架&#xff0c;用于构建基于MVC模式的Web应用程序。在 web 层框架历经 Strust1&#xff0c;WebWork&#xff0c;Strust2 等诸多产品的历代更选 之后&#xff0c;目前业界普…...

Mysql 语句

数据库管理 SQL语言分类 DDL 数据定义语言&#xff0c;用于创建数据库对象&#xff0c;如库、表、索引等 create 创建 create database/table; 数据库/表 create table 表名 &#xff08;括号内添加类型和字段&#xff09;;drop 删除 drop database/table; 数据库/表…...

软考高级架构师——6、软件架构设计

像学写文章一样&#xff0c;在学会字、词、句之后&#xff0c;就应上升到段落&#xff0c;就应追求文章的“布局谋 篇”&#xff0c;这就是架构。通俗地讲&#xff0c;软件架构设计就是软件系统的“布局谋篇”。 人们在软件工程实践中&#xff0c;逐步认识到了软件架构的重要性…...

虚拟内存相关笔记

虚拟内存是计算机系统内存管理的一个功能&#xff0c;它允许程序认为它们有比实际物理内存更多的可用内存。它使用硬盘来模拟额外的RAM。当物理内存不足时&#xff0c;操作系统将利用磁盘空间作为虚拟内存来存储数据。这种机制提高了资源的利用率并允许更大、更复杂的应用程序的…...

【linux】定时任务讲解

文章目录 一. 在某时刻只执行一次&#xff1a;at1. 设置定时任务2. 查看和删除定时任务 二. 周期性执行任务&#xff1a;cron1. 启动crond进程2. 编辑定时任务3. 查看和删除4. 用户权限4.1. 黑名单4.2指定用户 三. /etc/crontab的管理 一. 在某时刻只执行一次&#xff1a;at 1…...