函数式编程(四)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类型的结果,因为流中可能为空。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/63711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

朝夕光年游戏自动化测试实践

朝夕光年是面向全球用户与开发者的游戏研发与发行业务品牌&#xff0c;致力于服务全球玩家&#xff0c;帮助玩家在令人惊叹的虚拟世界中一起玩耍与创造。 在游戏的研发过程中&#xff0c;游戏自动化一直是开展难度较大的工程&#xff0c;具体包括机房机架、设备调度、软件框架、…

IntelliJ IDEA 简介

IntelliJ IDEA 简介 IntelliJ IDEA&#xff08;简称 IDEA&#xff09;是一款由 JetBrains 公司开发的强大且广受欢迎的集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于Java开发&#xff0c;但也支持其他编程语言和技术栈的开发。作为一款功能丰富、高效的IDE&am…

理解底层— —Golang的log库,二开实现自定义Logger

理解底层— —Golang的log库&#xff0c;实现自定义Logger 1 分析实现思路 基于golang中自带的log库实现&#xff1a;对日志实现设置日志级别&#xff0c;每天生成一个文件&#xff0c;同时添加上前缀以及展示文件名等 日志级别&#xff0c;通过添加prefix&#xff1a;[INFO]、…

学生信息管理系统MIS(前端)

改造HTML文件 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>学生信息管理系统MIS</title><!-- link在HTML文件中,引入外部的css文件 rel的值是固定写法,stylesheet样式表href用来指定样式表的位置--><lin…

【LeetCode】剑指 Offer <二刷>(4)

目录 题目&#xff1a;剑指 Offer 09. 用两个栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;剑指 Offer 10- I. 斐波那契数列 - 力扣&am…

MySQL 5种索引应用

文章目录 简介一、聚集索引二、唯一索引三、聚集索引和唯一索引对比四、非唯一&#xff08;普通&#xff09;索引五、全文索引六、组合索引七、索引验证总结 简介 在本篇文章中&#xff0c;我们将学习MySQL中5种不同类型的索引及其应用场景&#xff0c;以及它们的优缺点。 一…

WIFI与BT的PCB布局布线注意事项

1、模块整体布局时&#xff0c;WIFI模组要尽量远离DDR、HDMI、USB、LCD电路以及喇叭等易干扰模块或连接座&#xff1b; 2、晶体电路布局需要优先考虑&#xff0c;布局时应与芯片在同一层并尽量靠近放置以避免打过孔&#xff0c;晶体走线尽可能的短&#xff0c;远离干扰源&…

【Ajax】发送跨域的POST请求时,浏览器会先发送一次OPTIONS请求,然后才发送原本的POST请求

当发送跨域的POST请求时&#xff0c;浏览器会先发送一次OPTIONS请求&#xff0c;这是因为浏览器的同源策略。OPTIONS请求被称为预检请求(pre-flight request)&#xff0c;它是CORS(跨源资源共享)机制中的一部分。 预检请求的目的是为了确保实际请求&#xff08;例如POST、PUT等…

【MetaAI】2023年MetaAI发布的开源模型和工具

MetaAI开源模型和工具 MetaAILlamaSegment AnythingDINOv2ImageBindMMSLimaVoiceboxMusicGenLlama 2AudioCraftSeamlessM4T MetaAI Meta 首席执行官扎克伯格表示&#xff0c;与其他研究者分享 Meta 公司开发的模型可以帮助该公司促进创新、发现安全漏洞和降低成本。他今年 4 月…

概念解析 | 量子机器学习:将量子力学与人工智能的奇妙融合

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:量子机器学习。 量子机器学习:将量子力学与人工智能的奇妙融合 量子增强机器学习:量子经典混合卷积神经网络 量子机器学习是量子计算和机器学习的结合,它利用量子力学的特…

Opencv-C++笔记 (18) : 轮廓和凸包

文章目录 一、轮廓findContours发现轮廓drawContours绘制轮廓代码 二.几何及特性概括——凸包(Convex Hull)凸包概念凸包扫描算法介绍——Graham扫描算法 相关API介绍程序示例轮廓集合及特性性概括——轮廓周围绘制矩形框和圆形相关理论介绍轮廓周围绘制矩形 -API绘制步骤程序实…

Python数据分析案例30——中国高票房电影分析(爬虫获取数据及分析可视化全流程)

案例背景 最近总看到《消失的她》票房多少多少&#xff0c;《孤注一掷》票房又破了多少多少..... 于是我就想自己爬虫一下获取中国高票房的电影数据&#xff0c;然后分析一下。 数据来源于淘票票&#xff1a;影片总票房排行榜 (maoyan.com) 爬它就行。 代码实现 首先爬虫获…

<AMBA总线篇> AXI总线协议介绍

目录 01 AXI协议简介 AXI协议特性 AXI协议传输特性 02 AXI协议架构 AXI协议架构 write transaction(写传输) read tramsaction(读传输) Interface and interconnect 典型的AXI系统拓扑 03 文章总结 大家好&#xff0c;这里是程序员杰克。一名平平无奇的嵌入式软件工程…

stable diffusion实践操作-提示词-图片结构

系列文章目录 stable diffusion实践操作-提示词 文章目录 系列文章目录前言一、提示词汇总1.1 图片结构11.2 图片结构21.3 图片结构3 二、总结 前言 本文主要收纳总结了提示词-图片结构。 一、提示词汇总 1.1 图片结构1 StylesArtistshudson river school哈得逊河学派alpho…

Python 接口测试之Excel表格数据操作方法封装

引言 我们在做接口测试&#xff0c;经常会用到excel去管理测试数据&#xff0c;对Excel的操作比较频繁&#xff0c;那么使用python如何操作Excel文件的读与写呢&#xff1f;由于之前讲的都是大的框框&#xff0c;没有讲这么小的模块使用&#xff0c;现在就化整为0的讲解。 读…

GPT带我学-设计模式-适配器模式

1 什么是适配器设计模式 适配器设计模式是一种结构性设计模式&#xff0c;用于在不兼容的接口之间进行转换。它允许将一个类的接口转换成客户端所期望的接口。 适配器模式包含以下几个角色&#xff1a; 目标接口&#xff08;Target&#xff09;&#xff1a;定义客户端所期望…

基于OpenCV+LPR模型端对端智能车牌识别——深度学习和目标检测算法应用(含Python+Andriod全部工程源码)+CCPD数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境OpenCV环境Android环境1. 开发软件和开发包2. JDK设置3. NDK设置 模块实现1. 数据预处理2. 模型训练1&#xff09;训练级联分类器2&#xff09;训练无分割车牌字符识别模型 3. APP构建1&#xff09;导入OpenCV库…

倾斜摄影文件读取,不使用第三方库

不使用第三方库读取倾斜摄影文件 github地址 百度云 链接: https://pan.baidu.com/s/1v0kSzyXpBYTmw0ZOr2wsJA?pwd83ad 提取码: 83ad

数据结构-第一期——数组(Python)

目录 00、前言&#xff1a; 01、一维数组 一维数组的定义和初始化 一维变长数组 一维正向遍历 一维反向遍历 一维数组的区间操作 竞赛小技巧&#xff1a;不用从a[0]开始&#xff0c;从a[1]开始 蓝桥杯真题练习1 读入一维数组 例题一 例题二​ 例题三 实战训…

在iPhone 15发布之前,iPhone在智能手机出货量上占据主导地位,这对安卓来说是个坏消息

可以说这是一记重拳&#xff0c;但似乎没有一个有价值的竞争者能与苹果今年迄今为止的智能手机出货量相媲美。 事实上&#xff0c;根据Omdia智能手机型号市场跟踪机构收集的数据&#xff0c;苹果的iPhone占据了前四名。位居榜首的是iPhone 14 Pro Max&#xff0c;2023年上半年…