函数式编程(四)Stream流使用
一、概述
在使用stream之前,先理解Optional 。
Optional是Java 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对象。 - 使用
IntStream、LongStream、DoubleStream的static方法创建有限流,可以使用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
- 使用
BufferedReader的lines方法从文件中获得行的流 Files类的操作路径的方法,如list、find、walk、lines等
// 使用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表示流中的元素类型,a和b表示要连接的两个流。
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对象中定义的逻辑,对流中的元素进行收集和组合。 - 最终返回一个包含收集结果的对象,可以是
List、Set、Map等。
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)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。
在Java Stream API中,有多个方法可用于将流中的元素收集到不同类型的集合或映射中。
toList()方法:
-
toList()方法将流中的元素收集到一个列表中。- 它返回一个包含流中所有元素的新列表。
toSet()方法:
-
toSet()方法将流中的元素收集到一个集合中。- 它返回一个包含流中所有元素的新集合。
toMap()方法:
-
toMap()方法将流中的元素收集到一个映射中。- 它接受两个
Function函数式接口作为参数,用于提取键和值,并返回一个包含流中所有键值对的新映射。
toCollection()方法:
-
toCollection()方法将流中的元素收集到指定类型的集合中。- 它接受一个
Supplier函数式接口作为参数,用于提供一个自定义的集合实例。
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中,有多个终止操作方法可以用于对流中的元素进行计数、平均值、最大值、最小值、求和和统计等操作。
count()方法:
-
count()方法返回流中的元素数量。- 它返回一个
long类型的结果,表示流中的元素数量。
averagingXxx()方法:
-
averagingXxx()方法用于计算流中元素的平均值,其中Xxx可以是Int、Long或Double。- 它返回一个
double类型的结果,表示流中元素的平均值。
maxBy()和minBy()方法:
-
maxBy()方法接受一个Comparator函数式接口作为参数,返回流中的最大元素。minBy()方法接受一个Comparator函数式接口作为参数,返回流中的最小元素。- 它们返回一个
Optional对象,表示流中的最大或最小元素。
summingXxx()方法:
-
summingXxx()方法用于对流中的元素进行求和,其中Xxx可以是Int、Long或Double。- 它返回一个
Xxx类型的结果,表示流中元素的总和。
summarizingXxx()方法:
-
summarizingXxx()方法用于对流中的元素进行统计,其中Xxx可以是Int、Long或Double。- 它返回一个包含元素数量、总和、平均值、最大值和最小值的
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之前,先理解Optional 。 Optional是Java 8引入的一个容器类,用于处理可能为空的值。它提供了一种优雅的方式来处理可能存在或不存在的值,避免了空指针异常。 Optional的主要特点如下: 可能为空ÿ…...
区块链面临六大安全问题 安全测试方案研究迫在眉睫
区块链面临六大安全问题 安全测试方案研究迫在眉睫 近年来,区块链技术逐渐成为热门话题,其应用前景受到各国政府、科研机构和企业公司的高度重视与广泛关注。随着技术的发展,区块链应用与项目层出不穷,但其安全问题不容忽视。近年…...
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内核在不同的芯片架构、不同的板卡上运行起来,能够具备线程管理和调度,内存管理,线程间同步等功能。 移植可分为CPU架构移植和BSP(Board support package,板级支持包)移植两部…...
springboot中entity层、dto层、vo层通俗理解三者的区别
entity:这个类的属性是跟数据库字段一模一样的(驼峰命名),当我们使用MyBatis-Plus的时候经常用得到。 dto:用于后端接收前端返回的数据,一般是post请求,前端会给我们返回一个json对象ÿ…...
TypeScript_队列结构-链表
队列 队列(Queue),它是一种受限的线性表,先进先出(FIFO First In First Out) 受限之处在于它只允许在队列的前端(front)进行删除操作而在队列的后端(rear)进…...
STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库
通过DMA方式输出PWM模拟LED数据信号 优点:不消耗CPU资源 缺点:占用内存较大 STM32CUBEMX配置 定时器配置 定时器通道:TIM3 CH2 分频:0 重装值:79,芯片主频64Mhz,因此PWM输出频率:…...
记录错误:Access denied for user ‘root‘@‘localhost‘ (using password:No) 解决方案
他说我没输入密码,但是我输入了啊??于是,我试了试这儿,password 一改就好了。。。 他原来是是我打的很快,快速生成的。。。。...
python爬虫实战(5)--获取小破站热榜
1. 分析地址 打开小破站热榜首页,查看响应找到如下接口地址 2. 编码 定义请求头 拿到标头 复制粘贴,处理成json 处理请求头代码如下: def format_headers_to_json():f open("data.txt", "r", encoding"utf-8") # 读…...
单目标应用:基于麻雀搜索算法SSA的微电网优化调度MATLAB
一、微网系统运行优化模型 参考文献: [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、麻雀搜索算法简介 麻雀搜索算法 (Sparrow Search Algorithm, SSA) 是一种新型的群智能优化算法,于2020…...
C# easymodbus
库介绍 EasyModbus是用于 .NET 和 Java 平台上的Modbus TCP/UDP/RTU通讯协议库,支持多种编程语言,如C#、VB.NET、Java、C 与更多C#的变体,如Unity、Mono、.NET Core等等。 EasyModbus的Java版本至少需要Java 7,而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简单的事没说清,一片混乱,似懂非懂. mod语句查找只有一条规则:先找mod名1.rs,没有就我同名文件夹下的mod名1.rs,如果没有,就同名文件夹下的mod名1/mod.rs,再没有就error. 在mod.rs中,pub mod 文件…...
【Java核心知识】ThreadLocal相关知识
ThreadLocal 什么是ThreadLocal ThreadLoacal类可以为每个线程保存一份独有的变量,该变量对于每个线程都是独占的。实现原理为每个Thread类中包含一个ThreadHashMap,key为变量的对应的ThreadLocal对象,value为变量的值。 在日常使用中&…...
《Python基础教程(第三版)》阅读笔记 1
目录 1 快速上手:基础知识2 列表和元组3 字符串4 字典5 条件、循环及其他6 抽象7 再谈抽象8 异常9 魔法方法、特性和迭代器10 开箱即用 本文参考自《Beginning Python: from novice to professional》,中文版为《Python基础教程(第三版&#…...
坦克400 Hi4-T预售价28.5万元起,越野新能源好理解
8月25日,在以“智享蓉城,驭见未来”为主题的成都国际车展上,坦克品牌越野新能源再启新程,首次以全Hi4-T新能源阵容亮相展台,释放坦克品牌加速布局越野新能源的强烈信号。 Hi4-T架构首款落地车型坦克500 Hi4-T上市至今斩…...
我的Vim学习笔记(不定期更新)
2023年9月3日,周日上午 学到了啥就写啥,不定期更新 目录 字体 文件 标签页 分屏 调用系统命令 字体 设置字体大小 :set guifont字体:h字体大小 例如,:set guifontMonospace:h20 查询当前使用的字体和字体大小 :set guifont? 查看…...
spring boot项目生成容器并运行
一个安静的周末,shigen又睡懒觉了,上次说的拖延症的惩罚来了:早晚各100个健腹轮练习,早上的已经完成了。今天的文章来的有点晚,但是依旧保持质量。 springboot项目生成容器并运行 背景 将springboot项目打包成jar包&…...
Vue之html中特殊符号的展示
Vue之html中特殊符号的展示 在html中使用特殊字符时直接展示会报错,需要使用实体名称或者实体编号才能展示。 最常用的字符实体 显示结果 描述 实体名称 实体编号空格 < 小于号 < &…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
