Java8属于读书笔记系列,版权归《Java 8实战》,请勿商用。欢迎转载!!
Stream 支持的操作
- 筛选、切片和匹配
- 查找、匹配和归
- 使用数值返回等数值流
- 从多个源创建流
- 无限流
1 筛选和切片
用谓词筛选
谓词:一个返回boolean的函数
filter方法。使用谓词筛选一个流。List<Dish> vegetarianMenu = menu .stream() .filter(Dish::isVegetarian) .collect(toList()); vegetarianMenu.forEach(dish -> System.out.println(dish)); }
- 筛选各异的流
distinct(),返回一个元素各异(根据流所生成元素的hashCode和equals)的流
eg: 去重
public static void distinctMethod(){
List<Integer> numbers = Arrays.asList(1,2,1,4,5,6,3,2,6,7,9,0);
numbers.stream()
.distinct()
.forEach(System.out::println);
}
截短流
limit(long n) ,最多返回前 n 个元素。// 有序的集合 public static void limitMethod(){ List<Integer> numbers = Arrays.asList(1,2,3,56,59,7,8,4,3,2,1,0); numbers.stream() .distinct() .filter(n -> n < 10) .limit(5) .forEach(System.out::println); } //无序的集合 public static void limitMethodSet(){ Set<Integer> set = new HashSet<>(10); set.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,0)); set.stream() .limit(5) .forEach(System.out::print); }
跳过元素
skip(n),返回一个扔掉了前n个元素的流public static void skipMethod(){ List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7); numbers.stream() .limit(5) .skip(2) .forEach(System.out::println); System.out.println("------"); numbers.stream() .skip(2) .limit(5) .forEach(System.out::println); } //注意结果,skip与limit的顺序很重要!!!
2 映射
一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL里,你可以从表中选择一列。Stream API也通过 map 和 flatMap 方法提供了类似的工具。
- 对流中的每一个元素引用函数
public static void mapBasic(){
//获取每道菜的名字
List<String> strings = Dish.menu
.stream()
.map(Dish::getName) // 生成了Stream<String>
.collect(Collectors.toList());
strings.forEach(System.out::println);
//获取每道菜的名字的长度,map.map...
List<Integer> nameLength = Dish.menu
.stream()
.map(Dish::getName)
.map(String::length)
.collect(Collectors.toList());
nameLength.forEach(System.out::print);
}
- 流的扁平化flatMap
3 查找和匹配
另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。Stream
API通过 allMatch 、 anyMatch 、 noneMatch 、 findFirst 和 findAny 方法提供了这样的工具。
1. 至少匹配一个
匹配所有的元素
一个都没有匹配上
findFirst 返回的结果是Opional
- findAny 在并行时限制更少 返回的结果是Opional
- Optional 简介
Optional是一个容器类,可以用来替代null
4 归约(折叠fold)
常见的终端操作
返回boolean-->match操作
返回void --> foreach
返回Optional--> findAny findFirst等操作
返回集合 --> collect
以及更加复杂的操作:reduce,更复杂的查询操作!!!
归约操作(reduce()) :把流中的元素反复结合起来,形成一个值。
1. 元素求和
```
public static void getSum(List<Integer> numbers){
// 初始值为0
int sum = numbers.stream().reduce(0,(x,y) -> x+y);
System.out.println(sum);
int sum2= numbers.stream().reduce(0,Integer::sum);
System.out.println(sum2);
//无初始值,返回Optional,null是抛出NoSuchElementException("No value present").
Optional<Integer> sum3 = numbers.stream().reduce(Integer::sum);
System.out.println(sum3);
System.out.println(sum3.get()); //通过get() 获得具体的结果。
}
```
2. 最大值和最小值
```
public static void getMax(List<Integer> numbers){
Optional<Integer> max = numbers.stream().reduce(Integer::max);
System.out.println(max+" : " + max.get());
}
public static void getMin(List<Integer> numbers){
Optional<Integer> min = numbers.stream().reduce(Integer::min);
System.out.println(min + " : " + min.get());
}
```
3. map-reduce
```
public static void getSumByMapAndReduce(){
int count = Dish.menu.stream()
.map(dish -> 1) //先映射
.reduce(0,Integer::sum); //归约求值
System.out.println(count);
}
```
4. 数值流
Java 8引入了三个原始类型特化流接口来解决这个问题: IntStream 、 DoubleStream 和LongStream ,分别将流中的元素特化为 int 、 long 和 double ,从而避免了暗含的装箱成本。
4.1 原始类型流特化
1. 映射到数值流
将流转换为特化版本的常用方法是mapToInt、mapToDouble 和 mapToLong 。这些方法和前面说的 map方法的工作方式一样,只是它们返回的是一个特化流。
```
int calories = menu.stream()
.mapToInt(Dish::getCalories) // 返回一个 IntStream
.sum(); //求和,如果流是空的,sum默认值为0
```
2. 转换流对象
原始流转换成一般流(每个int都会装箱成一个Integer ),可以使用 boxed 方法:
```
public static void toStream(){
IntStream calories = menu.stream()
.mapToInt(Dish::getCalories); // 返回一个 IntStream
Stream<Integer> boxed = calories.boxed(); // 转换成一个流
}
```
3. 默认值OptionalInt
Optional 类,这是一个可以表示值存在或不存在的容器。 Optional 可以用Integer 、 String 等参考类型来参数化。对于三种原始流特化,也分别有一个 Optional原始类型特化版本: OptionalInt 、 OptionalDouble 和 OptionalLong 。
```
//例如,要找到 IntStream 中的最大元素,可以调用 max 方法,它会返回一个 OptionalInt :
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
//如果没有最大值,可以显示的处理。提供一个默认的最大值
int max = maxCalories.orElse(1);
```
4.2 数值范围
```
public static void toRange(){
IntStream.rangeClosed(1,100)//生成1到100,包含100
.filter(i -> i%2 == 0) //偶数
.forEach(System.out::println);
IntStream.range(1,100) // [1,100),不包含100
.filter(i -> i%2 == 0)
.forEach(System.out::println);
}
```
4.3 数值流应用:勾股数
```
public static void toPythagoreanTriples(){
IntStream.rangeClosed(1,100) //[1,100]
.boxed() //生成Stream<Integer>
.flatMap(i ->
IntStream.rangeClosed(i,100)
.mapToObj(j -> new double[]{i,j, Math.sqrt(i*i+j*j)})
.filter(t-> t[2]%1 == 0)
).forEach(t -> System.out.println(t[0]+", "+t[1]+", "+t[2]));
}
```
5. 构建流
5.1 由值创建流
```
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
//获得一个empty流
Stream<String> emptyStream = Stream.empty();
```
5.2 数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
IntStream intStream = Arrays.stream(numbers);
Stream<Integer> i = intStream.boxed();
5.3 文件生成流
public static void toFileStream(){
long uniqueWords = 0;
try(Stream<String> lines = Files.lines(Paths.get("d:/data.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
System.out.println("uniqueWords : " + uniqueWords);
}
catch(IOException e){
}
}
5.4函数生产流:无限流
1. 迭代(有序)
public static void toEndlessStream(){
Stream.iterate(0,n -> n+2) //无限流 正偶数
.limit(20)
.forEach(System.out::println);
}
public static void toFibonacciStream(){
Stream.iterate(new int[]{0,1}, t-> new int[]{t[1],t[0]+t[1]}) //斐波那契数 数组->新数组
.limit(20)
.forEach(t -> System.out.println("(" + t[0] + "," + t[1] +")"));
}
2. 生成(无序)
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);