【jdk8新特性】Stream API,包看包会
Stream API概述什么是Stream流流的操作的三个步骤创建流中间操作筛选和切片映射排序查找与匹配规约与收集终止操作(终端操作)博客地址(点击即可访问)github源码地址Lamda表达式https://github.com/zz1044063894/lambda函数式接口https://github.com/zz1044063894/methodInterface方法引用和构造器引用htt
概述
-
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
-
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
-
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
-
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
-
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
什么是Stream流
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
集合讲的是数据,流讲的是的计算.
注意:
- Stream不会自己储存元素
- 不会改变源对象。会产生一个新的持有结果的Stream
- Stream操作是延迟执行的,也就是说他们等到需要结果的时候才执行
流的操作的三个步骤
创建流
一个数据源(如:集合,数组),获取一个流
在 Java 8 中, 集合接口有两个方法来生成流:
-
stream() − 为集合创建串行流。
-
parallelStream() − 为集合创建并行流。
/**
* 创建流
*/
@Test
public void createStream() {
/**
* 1。
* 用collection集合的stream() − 为集合创建串行流
*
* 或者用collection集合的parallelStream() − 为集合创建并行流。
*/
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
/**
* 2。通过Arrays中的静态方法stream()获取数组流
* 这里因为不想创建对象了,就使用了一个系统对象夫类Object
*/
Object[] objects = new Object[10];
Stream<Object> stream2 = Arrays.stream(objects);
/**
* 3。通过Stream类中的静态方法of
*/
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
/**
* 4.创建无限流
*/
//迭代产生无限流
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
//查看结果,用了中间流
stream4.limit(10)
.forEach(System.out::println);
//生成无限流
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
中间操作
一个中间操作链,对数据源的数据进行处理
多个中间操作可以连接起来作为一个流水线,除非流水线上中断中间操作,否则中间操作不会执行任何处理
,只会在终端操作的时候一次处理。称为“惰性求值”。
筛选和切片
- filter 用于通过设置的条件过滤出元素,接受参数是Lambda表达式,类似于SQL语句的where条件内容
/**
* 筛选与切片
*/
@Test
public void test(){
List<User> users = Arrays.asList(
new User("张三",24),
new User("李四",15),
new User("王五",16),
new User("赵六",27),
new User("田七",18)
);
Stream<User> stream = users.stream()
.filter(e->{
System.out.println("Stream API中间操作");
return e.age>=18;
});
//终止操作
stream.forEach(System.out::println);
}
class User{
String name;
int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
运行结果:
可以看出,每次比较条件是都会运行,输出中间操作是为了证明如果没有终止操作,那么系统就不会运行中间操作,只是将操作记录下来,并没有执行
- limit 用于获取指定数量的流 类似于SQL语句的 limit
- skip(n) 跳过元素 类似于SQL语句的offset
- distinct 通过元素的hashcode和equals判断是否想等,相等的内容只返回一个,类似于SQL关键字distinct
- sorted 用于对流进行排序,类似与SQL语句的order by
映射
- map 用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
/**
* 映射
*/
@Test
public void test1() {
List<String> list = Arrays.asList("aaa", "bbbb", "ccc");
list.stream()
.map((x) -> x.toUpperCase())
.forEach(System.out::println);
}
运行结果:
这里map操作字符串,将字符串转换为大写,然后赋给新流,笔者没有直接拿新流接收,直接输出流。
- flatMap 类似于map操作,但是将可能存在的多个流转换成一个流存储,可以参考对象里包含对象,map如果操作的返回值是一个对象,map本身也会有一个对象来存储map的结果,这就操作了对象中包含对象,也就是流中有流,flatmap会取到对象中的元素,然后把这些元素构成一个新的流。(也可以类似与list的add(Objcet)和addAll(collection) add是将object作为一个新元素放进去,而addAll是把集合的元素拆分出来依次放进去)
排序
- sorted 用于对流进行排序,默认按照compareTo方法排序,也可以自己定义排序方式。
/**
* 排序
*/
@Test
public void test2() {
List<String> list = Arrays.asList("ddd", "aaa", "bbbb", "ccc");
System.out.println("---------自然排序");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("---------zidingyi排序");
list.stream()
.sorted((a,b)->{
if(a.equals(b)){
return a.compareTo(b);
}else {
return -a.compareTo(b);
}
})
.forEach(System.out::println);
}
结果:
笔者为了省事,就直接倒序排了一下,只要在sort内部写好你要排序的方法,就可以自己实现排序规则了。
查找与匹配
-
allMatch-检查是否匹配所有元素
-
anyMatch-检查是否至少匹配- - -个元素noneMatch-检 查是否没有匹配所有元素findFirst-返 回第一个元素
-
findAny-返回当前流中的任意元素count-返 回流中元素的总个数
-
max-返回流中最大值
-
min-返 回流中最小值
/**
* 查找与匹配
*/
@Test
public void test3(){
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
}
结果:
规约与收集
- reduce 规约reduce(T identity, BinaryOperator) / reduce(BinaryOperator) -可以将流中元素反复结合起来,得到一个值。
/**
* 规约
*/
@Test
public void test4(){
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
//确定有初始值的情况接收
Integer sum = numbers.stream()
.reduce(0,(x,y)->x+y);
System.out.println(sum);
//不确定有没有初始值
Optional<Integer> optionalInteger = numbers.stream()
.reduce(Integer::sum);
System.out.println(optionalInteger.get());
}
结果:结果都是25,因为程序比较简单,就不贴图了
- conllector collect-将流转换为 其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法*/
/**
* 收集
*/
@Test
public void test5(){
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
}
结果:
筛选列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
终止操作(终端操作)
一个终止操作,执行中间操作链,运算得出结果
终端操作就是使用中间操作得到结果的过程,上面的例子已经充分说明了,笔者就不再过多讲述了
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)