4 使用流
4.1 筛选和切片
谓词做筛选(忽略流中的数据),截短到指定长度教切片
4.1.1 用谓词筛选
4.1.2 筛选各异的元素
4.1.3 截短流
4.1.4 跳过元素
4.2 映射
一个非常常见的映射就是获取对象中的属性值,比如在sql中选择一列,Streamapi也提供了map和flatmap方法提供了类似的工具
4.2.1 对流中每一个元素应用函数
List<Dish> dishes = Arrays.asList(new Dish("asda",false,1, Dish.Type.MEAT),new Dish("1",false,1, Dish.Type.MEAT),new Dish("2",false,1, Dish.Type.MEAT));
List<String> dishNames = dishes.stream().map((Dish dish) -> dish.getName()).collect(Collectors.toList())
// 获取字符串长度
List<String> words = Arrays.asList("a12","asd","lamdba","adsasd");
List<Integer> wordsLength = words.stream().map(String::length).collect(Collectors.toList());
4.2.2 流的扁平化
扁平化就是将每个流化成单个流
1.尝试使用map和Arrays.stream
List<String> list1 = Arrays.asList("Hello", "World");
// 以下代码报异常,因为map返回的是流的列表,并不是流的内容
List<String> dd = list1.stream().map((String s) -> s.split(""))
.map(Arrays::stream)
.distinct()
.collect(toList());
使用flatmap
List<String> list1 = Arrays.asList("Hello", "World");
List<String> dd = list1.stream().map((String s) -> s.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
4.3 测验
// 你可以利用map方法的Lambda,接受一个数字,并返回该数字的平方的Lambda来ᝌ
//解决这个问题。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares =
numbers.stream()
.map(n -> n * n)
.collect(toList());
squares.forEach(item -> System.out.println(item));
// 测验二
/* 给定两个数字列表,如何返回所有的数对呢?例如给定列表1[1, 2, 3]和表[3, 4],应
该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单期起见,你可以用有两个元素的数组来代
表数对。*/
List<Integer> number1 = Arrays.asList(1,2,3);
List<Integer> number2 = Arrays.asList(3,4);
List<int[]> pairs =
number1.stream()
.flatMap(i -> number2.stream()
.map(j -> new int[]{i,j})
)
.collect(toList());
pairs.parallelStream().forEach((int[] i) -> System.out.println(i));
// 测试三
List<int[]> pairs1 =
number1.stream()
.flatMap((Integer i) -> number2.stream()
// 过滤
.filter((Integer j) -> (i + j) % 3 == 0)
.map((Integer j) -> new int[]{i,j})
)
.collect(toList());
System.out.println(pairs1);
4.4 查找和匹配
4.4.1 检查谓词是否至少匹配一个元素
说白了就是是否包含某个元素
anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词”。比如,你可以用它来看看菜单里面是否有素食可选择:
// 检查谓词是否至少匹配一个元素
if(dishes.stream().anyMatch(Dish::isVegetarian)){
System.out.println("找到素食");
}
4.4.2 检查谓词是否匹配所有元素
allMatch方法的工作原理和anyMatch类似,但它会看看流中的元素是否都能匹配给定的谓词。比如,你可以用它来看看菜品是否有利安全(即所有热量都低于1000卡路里)
// 检查谓词是否匹配所有元素
boolean isHealthy = dishes.stream().allMatch((Dish dish) -> dish.getCalories() < 1000);
4.4.3 查找元素
// 查找元素
Optional<Dish> dish =
dishes.stream()
.filter(Dish::isVegetarian)
.findAny();
Optional简介
4.4.4 查找第一个元素
4.5 规约
4.5.1 元素求和
// 求和
int sumCalories = dishes.stream().map(Dish::getCalories).reduce(0,(int a,int b) -> {
return a + b;
});
4.5.2 最大值与最小值
// 计算最大值
Optional<Integer> maxCalories = dishes.stream().map(Dish::getCalories).reduce(Integer::max);
Optional<Integer> minCalories1 = dishes.stream().map(Dish::getCalories).reduce((Integer x,Integer y) -> x < y ? x : y);
int dishNum = dishes.stream().map(d -> 1).reduce(0,Integer::sum);
System.out.println(dishNum);
4.6 付诸实践
在本节中,你会将迄今学到的关于流的知付诸诸实践。我们来看一个不同的领域:执行交易的交易员。你的经理让你为八个查询找到答案。你能做到吗?
(1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。
(2) 交易员都在哪些不同的ۡ市工作过?
(3) 查找所有来自剑桥的交易员,并按ހ名排序。
(4) 返回所有交易员的姓名字符串,按字母顺序排序。
(5) 有没有交易员是在米兰工作的?
(6) 打印生活在剑桥的交易员的所有交易额。
(7) 所有交易中,最高的交易额是多少?
(8) 找到交易额最小的交易。
package com.java.lamdba.four;
import lombok.Data;
/**
* 交易员类
*/
@Data
public class Trader {
private final String name;
private final String city;
public Trader(String n, String c){
this.name = n;
this.city = c;
}
}
package com.java.lamdba.four;
import lombok.Data;
/**
* 交易类
*/
@Data
public class Transaction {
private final Trader trader;
private final int year;
private final int value;
public Transaction(Trader trader, int year, int value){
this.trader = trader;
this.year = year;
this.value = value;
}
}
package com.java.lamdba.four;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toSet;
/**
* 测试类
*/
public class TradDemo {
public static void main(String[] args) {
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario","Milan");
Trader alan = new Trader("Alan","Cambridge");
Trader brian = new Trader("Brian","Cambridge");
List<Transaction> transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
// 1.找出2011年的所有交易并按交易额排序(从低到高)
List<Transaction> tr2011 = transactions.stream().filter(d -> d.getYear() == 2011)
.sorted(Comparator.comparing(Transaction::getValue))
.collect(Collectors.toList());
// 2.交易员都在哪些不同的城市工作过
List<String> cities = transactions.parallelStream().map((Transaction transaction) -> transaction.getTrader().getCity())
.distinct()
.collect(Collectors.toList());
Set<String> cities1 =
transactions.stream()
.map(transaction -> transaction.getTrader().getCity())
.collect(toSet());
// 3.查找所有来自于剑桥的交易员,并按姓名排序
List<Trader> traders = transactions.stream().map(Transaction::getTrader)
.filter( d -> "Cambridge".equals(d.getCity()))
.distinct()
.sorted(Comparator.comparing(Trader::getName))
.collect(Collectors.toList());
// 4.返回所有交易员姓名的字符串并通过字母排序,按字母顺序排序
String nameStr = transactions.stream().map((Transaction t) -> t.getTrader().getName())
.distinct()
.sorted()
.reduce("",(String::concat));
String nameStr1 = transactions.stream().map((Transaction t) -> t.getTrader().getName())
.distinct()
.sorted()
.collect(Collectors.joining());
// 5.有没有交易员是在米兰工作的
boolean isMilan = transactions.stream().anyMatch(d -> d.getTrader().getCity().equals("Milan"));
// 6.打印生活在剑桥的交易员的所有交易额
transactions.stream().filter(item -> "Cambridge".equals(item.getTrader().getCity()))
.map(item -> item.getValue())
.forEach(item -> System.out.println("交易额为" + item));
// 7. 所有交易中,最高的交易额是多少
Optional<Integer> maxValue = transactions.stream().map(item -> item.getValue()).reduce(Integer::max);
// 8. 所有交易中,最少的交易额是多少
Optional<Integer> minValue = transactions.stream().map(item -> item.getValue()).reduce(Integer::min);
}
}
4.7 数值流
4.7.1 原始类型流特化
IntStream,DoubleStream,LongStream作为原始类型特化流,顾名思义就是将流中的元素转成int,double,long,从而避免的装箱操作
-
映射到数值流
-
转回对象流
-
默认值OptionalInt
4.7.2 数值范围
和数字打交道时,有一个常用的东西就是数值范围。比如,假设你想要生成1和100之间的所有数字。Java 8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:
range和rangeClosed。这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但range是不包含结束值的,而rangeClosed则包含结束值。让我们来看一个例子:
4.7.3 数值流的应用,勾股数
// // 生成勾股数
// Stream<int []> gouguStream = IntStream.rangeClosed(1,100)
// // 生一个勾股数形成一个流
// .boxed()
// .flatMap(a -> IntStream.rangeClosed(a,100).filter(b -> Math.sqrt( a*a + b*b) % 1==0)
// .mapToObj(b -> new int[]{a,b, (int) Math.sqrt(a*a + b*b)})
// );
// gouguStream.forEach(item -> System.out.println(item[0] + "," + item[1] + "," + item[2]));
Stream<double []> gouguStream2 = IntStream.rangeClosed(1,100)
// 生一个勾股数形成一个流
.boxed()
.flatMap(a -> IntStream.rangeClosed(a,100)
.mapToObj(b -> new double[]{a,b, Math.sqrt(a*a + b*b)})
.filter(item -> item[2] % 1 ==0 )
);
gouguStream2.forEach(item -> System.out.println(item[0] + "," + item[1] + "," + item[2]));
4.8 构建流
4.8.1 由值创建流
4.8.2 由数组创建流
4.8.3 由文件创建流
4.8.4 由函数生成流:创建无限流
package com.java.lamdba.four;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.IntSupplier;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StructStreamDemo {
public static void main(String[] args) {
// 由值创建流
Stream<String> stringStream = Stream.of("java8", "lamdba", "adasdasd");
stringStream.map(String::toUpperCase).forEach(item -> System.out.println(item));
// 创建空流
Stream<String> emptyStream = Stream.empty();
// 数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
// 由文件生成流
long uniqueWords = 0;
try {
Stream<String> stringStream1 = Files.lines(Paths.get("E:\\learn\\java_learning\\design\\lamdba\\src\\main\\resources\\data.txt"), Charset.defaultCharset());
uniqueWords = stringStream1.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
System.out.println(uniqueWords);
} catch (Exception ex) {
System.out.println(ex);
}
// c由函数创建无限流,
// 1.迭代
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
// 斐波那契数列
// Stream.iterate(new int[]{0, 1},
// t -> new int[]{t[1],t[0] + t[1]})
// .limit(10)
// .map(t -> t[0])
// .forEach(System.out::println);
IntSupplier fib = new IntSupplier(){
private int previous = 0;
private int current = 1;
@Override
public int getAsInt(){
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib).limit(10).forEach(System.out::println);
}
}