Java8_Stream流
一、什么是Stream流?
定义:流就是由支持数据处理的源数据生成的元素序列
流的特点:
java.util.Stream
流是一系列数据项,它不是一种数据结构。- Stream 的创建需要指定一个数据源, 比如 集合、数组, 不支持Map。
- 每次生成的流,只能使用(消费)一次。(二次使用报:stream has already been operated upon or closed
- Stream 的操作可以串行执行或者并行执行。
流与集合的区别:
- 集合是一个内存中的数据结构,集合里面的元素都的先算出来才能添加到集合中
- 而流的话,它像一个延迟创建的集合,只有一个在消费者要求的时候才会计算值(也就是–>按需生成
- 流的话是内部迭代,集合是外部迭代
二、流的使用
1)流的创建
源 | 写法 |
---|---|
从Collection(集合)中构建 | list.stream() |
由值创建流 | Stream.of(v1,v2,v3) |
从数组中构建 | Arrays.stream(arr) |
从文件中构建 | Files.lines(path) |
由函数生成 | 创建无限流 |
1)从集合中获取流
//从集合中获取流
@Test
public void fromCollection() {
List<String> list = new ArrayList<>(Arrays.asList("a","b"));
Stream<String> stream = list.stream();
}
2)使用Stream.of()
获取流
Stream<Integer> stream = Stream.of(1, 2, 3);
3)使用Arrays.stream()
从数组中获取流
@Test
public void fromArrays() {
String[] arr = {"aa", "ee", "ii"};
Stream<String> stream = Arrays.stream(arr);
}
4)从文件中获取流
@Test
public void fromFile() throws IOException {
Path path = Paths.get("D:\\IdeaProjects\\DataStructure\\Data\\src\\pers\\xu\\lambda\\LambdaDemo.java");
Stream<String> stream = Files.lines(path);
stream.forEach(System.out::println);
}
5)使用函数创建无效流
有2种方法:
Stream.iterate():接受一个初始值,以及一个依次引用在每个产生的新值上的Lambda
Stream.generate():接受一个Supplier类型的Lambda提供新的值
@Test
public void fromIterate() {
// 从2开始,后面 n *= 2 2,4,8
Stream<Integer> stream1 = Stream.iterate(2, n -> n*2).limit(3);
// 使用 generate()
Stream<Double> stream2 = Stream.generate(Math::random).limit(3);
}
6)示例:创建一个Apple对象的无限流
// 自定义类实现 供给型接口
class AppleSupplier implements Supplier<Apple>{
private final Random random = new Random();
String[] colors = {"red","green","blue","yellow","white"};
@Override
public Apple get() {
int size = random.nextInt(100);
return new Apple(size,colors[random.nextInt(colors.length)]);
}
}
@Test
public void test21(){
Stream.generate(new AppleSupplier())
.limit(6)
.sorted(Comparator.comparingInt(Apple::getSize))
.forEach(System.out::println);
}
// 输出结果
Apple{size=21, color='blue'}
Apple{size=30, color='white'}
Apple{size=64, color='red'}
Apple{size=65, color='yellow'}
Apple{size=75, color='white'}
Apple{size=99, color='green'}
1)中间操作
filter、limit、skip、map、flatMap的使用
- filter:过滤
- limit:返回一个不超过给定长度的流;
- skip:返回一个扔掉了前n 个元素的流;【skip(k):跳过前k个元素
- map:将流中元素其映射成一个新的元素
- flatMap:把一个流中的每个值都换成另一个流,然后把所有的流连接
- peek:接受一个Consumer接口。对当前流中的元素进行操作,返回的原来的流
- distinct:去重
- sorted:排序
1)filter():过滤
接收一个断言式接口(Predicate)作为参数
@Test
public void testFilter(){
List<Integer> nums = Arrays.asList(10, 2, 2, 5, 6, 6, 3);
//从流中选出偶数,且没有重复
nums.stream().filter(i -> i%2==0) //过滤
.distinct() //去重
.sorted((x,y) -> y.compareTo(x)) //排序
.forEach(x -> System.out.println(x+"-"));
}
2)limit():截短流
该方法会返回一个不超过给定长度的流;
@Test
public void testLimit(){
String[] arr = {"aa","b","cc","d"};
Arrays.stream(arr).
limit(3). //取前3个
forEach(System.out::println);
}
3)skip():跳过元素
返回一个扔掉了前n 个元素的流
@Test
public void testSkip(){
String[] arr = {"aa","b","cc","d"};
Arrays.stream(arr).
skip(2). //跳过前2个 [aa,b
forEach(System.out::println);
//结果:cc d
}
4)map():映射
将字符串元素转换为它的长度
@Test
public void testMap2(){
List<String> strs = Arrays.asList("a", "bb", "ccc", "dd");
strs.stream().map(x -> x.length()) // 方法引用:map(String::length)
.collect(Collectors.toList())
.forEach(System.out::println);
}
5)flatmap():流的扁平化
示例:获取所有单词的字符表,过滤重复字符
@Test
public void test13(){
String[] arr = {"hello","TOM","go","Nothing"};
Stream<String> words = Arrays.stream(arr);
List<String> list = words.map(s -> s.split("")) // 映射为字符数组
.flatMap(Arrays::stream) // 将每个字符数组生成的流,扁平为一个流
.distinct()
.collect(Collectors.toList());
System.out.println(list);
}
// 输出结果:
[h, e, l, o, T, O, M, g, N, t, i, n]
6)peek()
接受一个Consumer接口,返回原来的流
public List<Apple> getList(){
List<Apple> list = new ArrayList<>();
list.add(new Apple(12,"red"));
list.add(new Apple(5,"deepRed"));
list.add(new Apple(9,"green"));
list.add(new Apple(14,"yellow"));
list.add(new Apple(3,"blue"));
list.add(new Apple(3,"red"));
return list;
}
@Test
public void test11(){
List<Apple> list = this.getList();
list.stream().peek(apple -> {
if("red".equals(apple.getColor())){
apple.setColor("pink");
}
}).forEach(System.out::println);
}
// 输出结果
Apple{size=12, color='pink'}
Apple{size=5, color='deepRed'}
Apple{size=9, color='green'}
Apple{size=14, color='yellow'}
Apple{size=3, color='blue'}
Apple{size=3, color='pink'}
7)sorted()
把苹果按size排序
@Test
public void test12(){
List<Apple> list = this.getList();
list.stream().
sorted(Comparator.comparingInt(Apple::getSize)).
forEach(System.out::println);
}
// 输出结果
Apple{size=3, color='blue'}
Apple{size=3, color='red'}
Apple{size=5, color='deepRed'}
Apple{size=9, color='green'}
Apple{size=12, color='red'}
Apple{size=14, color='yellow'}
2)终端操作
终端操作会从流的流水线 生成结果。
不做终止操作,中间环节就不执行,体现的就是延迟加载的思想
- match:查看元素是否匹配(返回boolean),
allMatch()
: 检查是否匹配所有元素anyMatch()
: 检查是否至少匹配一个元素noneMatch()
: 检查是否与 所有元素 都不匹配- find:
isPresent()
将在Optional包含值的时候返回true, 否则返回false;- ifPresent(Consumer block)`会在值存在的时候执行给定的代码块;
T get()
会在值存在时返回值,否则抛出一个NoSuchElement异常;T orElse(T other)
会在值存在时返回值,否则返回一个默认值;Optional<T> of(T value)
: 通过value构造一个Optional;- max(Comparator c): 返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c):内部迭代
- findFirst():取集合第一个元素
- reduce():规约
- 把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。
- 从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。
- 例如 Stream 的 sum 就相当于
Integer sum = integers.reduce(0, (a, b) -> a+b);
- 也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional 。
1)count()
对流中的元素进行计数,返回一个long类型值
@Test
public void test18(){
int[] arr = {1,2,3,4,5,0};
long count = Arrays.stream(arr).filter(x -> x % 2 == 1).count();
System.out.println(count);
}
// 输出:3
2)match()
@Test
public void test17(){
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.println(arr.stream().allMatch(i -> i > 10));
System.out.println(arr.stream().anyMatch(i -> i > 6));
System.out.println(arr.stream().noneMatch(i -> i < 0));
}
// 输出
false
true
true
- reduce:规约
什么是规约?
就是将流中所有元素反复结合起来,得到一个值,比如一个Integer。称为规约
示例1:元素求和,最大值,最小值
@Test
public void test15(){
int[] arr = {1,2,3,4,5,0};
// 带起始种子
int sum1 = Arrays.stream(arr).reduce(0, (a,b)->a+b);
int sum2 = Arrays.stream(arr).reduce(0, (a,b)->Integer.sum(a,b));
int sum3 = Arrays.stream(arr).reduce(0, Integer::sum);
System.out.println("sum= "+sum1);
// 无起始种子,返回Option
OptionalInt sum4 = Arrays.stream(arr).reduce(Integer::sum);
sum4.ifPresent(System.out::println);
// 最大值
int max = Arrays.stream(arr).reduce(0, Integer::max);
System.out.println("max= "+max);
// 最小值
int min = Arrays.stream(arr).reduce(0, Integer::min);
System.out.println("min= "+min);
}
// 输出结果
sum= 15
15
max= 5
min= 0
4)min / max(Comparator c)
@Test
public void test19(){
List<Apple> list = this.getAppleList();
// size 最大的苹果
Optional<Apple> maxApple1 = list.stream().max(Comparator.comparingInt(Apple::getSize));
// size 最小的苹果
Optional<Apple> minApple2 = list.stream().min(Comparator.comparingInt(Apple::getSize));
}
三、收集器
一般来说,Collector会对元素应用一个转换函数,并将结果累积在一个数据结构里面,产生最终输出
具体的说,对流调用collect 方法将对流中的元素触发一个归约操作(由Collector 来参数化
主要提供3大功能:
- 将元素归约和汇总为一个值
- 元素分组
- 元素分区
1)查找流中元素最大值和最小值
- Collectors.maxBy():最大值
- Collectors.minBy():最小值
@Test
public void test19(){
List<Apple> list = this.getAppleList();
// size 最大的苹果
Optional<Apple> maxApple = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(Apple::getSize)));
// size 最小的苹果
Optional<Apple> minApple = list.stream().collect(Collectors.minBy(Comparator.comparingInt(Apple::getSize)));
}
2)汇总
Collectors.summingInt()
:总和Collectors.averagingInt()
:求平均Collectors.summarizingInt()
:数据汇总
@Test
public void test19(){
List<Apple> list = this.getAppleList();
// 对apple的size汇总
Integer sizeSum = list.stream().collect(Collectors.summingInt(Apple::getSize));
System.out.println("sum= "+sizeSum);
// 对apple的size求平均
Double avg = list.stream().collect(Collectors.averagingInt(Apple::getSize));
System.out.println("avg= "+avg);
// 对apple进行大汇总,包括个数,size最大,最小,平均,总和
IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(Apple::getSize));
System.out.println(summaryStatistics);
}
// 输出结果
count= 6
sum= 46
avg= 7.666666666666667
IntSummaryStatistics{count=6, sum=46, min=3, average=7.666667, max=14}
3)连接字符串
@Test
public void test20(){
List<Apple> list = this.getAppleList();
String str = list.stream().map(Apple::getColor).collect(Collectors.joining());
System.out.println(str);
}
// 输出结果
reddeepRedgreenyellowbluered
四、串行流与并行流
Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
使用:将 stream()
改为parallelStream()
即可