流的使用
总结
- Streams API可以表达复杂的数据处理查询。
- 可以使用filter、distinct、skip和limit对流做筛选和切ྞ。
- 可以使用map和flatMap提取或转换流中的元素。
- 可以使用findFirst和 findAny方法查找流中的元素。你可以用allMatch、
noneMatch和anyMatch方法让流匹配给定的谓词。 - 这些方法都利用了短路:找到结果就立即停止计算;没有必要处理整个流。
- 可以利用reduce方法将流中所有的元素迭代合并成一个结果,例如求和或查找最大元素。
- filter和map等操作是无状态的,它们并不存储任何状态。reduce等操作要存储状态才能计算出一个值。sorted和distinct等操作也要存储状态,因为它们需要把流中的所有元素缓存起来才能返回一个新的流。这种操作称为有状态操作。
- 流有三种基本的原始类型特化:IntStream、DoubleStream和LongStream。它们的操作也有相应的特化。
- 流不仅可以从集合创建,也可从值、数组、文件以及iterate与generate等特定方法创建。
筛选和切片
1、谓词
该操作会接受一个៊词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。例如,筛选出所有素菜,创建一张素食菜单:
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
2、去重
以下代码会筛选出列表中所有的偶数,并确保没有重复
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
3、限定
选出热量超过300卡路里里的头三道菜:
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
4、忽略
下面的代码将跳过超过300卡路里里的头两道菜,并返回剩下的
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
映射
一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL里,你可以从表中选择一列。Stream API也通过map和flatMap方法提供了类似的工具。
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流。
List<String> uniqueCharacters =
words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
查找和匹配
1、anyMatch、allMatch、noneMatch
流中是否有一个元素能匹配给定的谓词
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
看流中的元素是否都能匹配给定的谓词
boolean isHealthy = menu.stream()
.allMatch(d -> d.getCalories() < 1000);
保流中没有任何元素与给定的谓词匹配
boolean isHealthy = menu.stream()
.noneMatch(d -> d.getCalories() >= 1000);
2、findAny、findFirst
findAny方法将返回当前流中的任意元素。
Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny();
有序的流查找第一个元素
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst();
归约
1、求和
reduce接受两个参数:
一个原始值,这里是0;
一个BinaryOperator来将两个元素结合起来产生一个新值;
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
映射到数值流
IntStream、DoubleStream和LongStream对应mapToInt、mapToDouble和mapToLong避免装箱操作
int calories = menu.stream().mapToInt(Dish::getCalories).sum();
转换回对象流
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
2、最值
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);
3、范围(range和rangeClosed)
这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但
range是不包含结束值的,而rangeClosed则包含结束值。
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count());
构建流
1、值创建流
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
2、数组创建流
int sum = Arrays.stream(numbers).sum();
3、文件生成流
long uniqueWords = 0;
try(Stream<String> lines =
Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
}
catch(IOException e){
}
4、函数生成流
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
以上就是对流的基本使用。