Stream流
-
概述
说道Stream便容易想到I/O Stream, 而实际上,谁规定“流” 就一定是“IO流”呢?在Java8中,得益于Lambda所带来的函数式编程,引入一个全新的Stream概念,用于解决已有集合类库既有的弊端 -
传统集合遍历的弊端
需求:筛选出姓张且名字长度为3的人名并打印。public class Demo01 { public static void main(String[] args) { // 筛选出姓张且名字长度为3的人名并打印. List<String> nameList = new ArrayList<>(); nameList.add("张芷若"); nameList.add("赵敏"); nameList.add("鑫磊"); nameList.add("吴莫愁"); nameList.add("张天爱"); nameList.add("张广仁仁"); //存储的是姓张 List<String> nameList1 = new ArrayList<>(); for (String name : nameList) { if (name.startsWith("张")) { nameList1.add(name); } } System.out.println(nameList1); //存储的姓张且长度为3的名字 List<String> nameList2 = new ArrayList<>(); for (String name : nameList1) { if (3 == name.length()) { nameList2.add(name); } } System.out.println(nameList2); } }
通过以上代码发现,传统的for循环不仅要专注做什么,还要专注怎么做,Java8的Lambda让我们可以更贱专注于做什么(What),而不是怎么做(How)
public class Demo01 {
public static void main(String[] args) {
// 筛选出姓张且名字长度为3的人名并打印.
List<String> nameList = new ArrayList<>();
nameList.add("张芷若");
nameList.add("赵敏");
nameList.add("老邱");
nameList.add("吴莫愁");
nameList.add("张天爱");
nameList.add("张广仁仁");
nameList.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> 3 == name.length() )
.forEach(name -> System.out.println(name));
}
}
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流,过滤姓张、过滤长度为3、逐一打印。代码中并没有体现使用线性循环或是其他任何算法进行遍历,我们真正要做的事情是内容被更好地体现在代码中
流式思想
-
概述
整体来看,流式思想类似工厂车间的“生产流水线”。Stream(流)是一个来自数据源的元素队列是特定类型 的对象,形成一个队列。Java中Stream并不会存储元素,而是按需计算。数据源流的来源。可以是集合,数组等。备注:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据机构,其本身并不存储任何元素(或其地址值)。 -
流的获取
java.util.strean.Stream 是Java8 新加入的常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下两种常用的方式:- 所有的Collection集合都可以通过stream默认方法获取流
- stream接口的静态方法of可以获取数组对应的流
-
获取Collection对应的流
-
获取Map对应的流
将map拆分成健集合和值结合,在分别获取对应的流 -
获取数组对应的流
Stream常用方法
-
概述
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:- 延迟方法:返回值类型仍是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
- 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。比如:终结方法包括count和forEach方法。
-
逐一处理:forEach
public class Demo03 { public static void main(String[] args) { // 语法 // void forEach(Consumer<? super T> action); // 基本使用 List<String> nameList = new ArrayList<>(); nameList.add("张芷若"); nameList.add("赵敏"); nameList.add("鑫磊"); nameList.add("吴莫愁"); nameList.add("张天爱"); nameList.add("张广仁仁"); // Performs an action for each element of this stream. 终结方法 // 遍历流中的元素 nameList.stream().forEach(name -> System.out.println(name)); //判断流中的元素是否满足条件 nameList.stream().filter(name->name.startsWith("张")).forEach(name-> System.out.println(name)); } }
-
过滤filter
//语法 // Stream<T> filter(Predicate<? super T> predicate); // 基本使用 List<String> nameList = new ArrayList<>(); nameList.add("张芷若"); nameList.add("赵敏"); nameList.add("鑫磊"); nameList.add("吴莫愁"); nameList.add("张天爱"); nameList.add("张广仁仁"); long count = nameList.stream() .filter(name -> name.startsWith("张")); System.out.println(count);
-
统计个数:count
// 语法 // long count(); // 基本使用 List<String> nameList = new ArrayList<>(); nameList.add("张芷若"); nameList.add("赵敏"); nameList.add("鑫磊"); nameList.add("吴莫愁"); nameList.add("张天爱"); nameList.add("张广仁仁"); long count = nameList.stream() .filter(name -> name.startsWith("张")) .count(); System.out.println(count);
-
取前几个:limit
limit方法可以对流进行截取,只取用前n个。参数是一个long型,如果集合当亲长度大于参数则进行截取;否则不进行操作// 语法 // Stream<T> limit(long maxSize); // 基本使用 List<String> nameList = new ArrayList<>(); nameList.add("张芷若"); nameList.add("赵敏"); nameList.add("鑫磊"); nameList.add("吴莫愁"); nameList.add("张天爱"); nameList.add("张广仁仁"); // 前三个 // nameList.stream().limit(3).forEach(name-> System.out.println(name)); // 后三个 nameList.stream().skip(3).forEach(name-> System.out.println(name));
跳过几个:skip
如果希望跳过前几个元素,可以使用skip.方法获取一个截取之后的新流。如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流
// 语法
// stream<T> skip(long n);
// 基本使用
List<String> nameList = new ArrayList<>();
nameList.add("张芷若");
nameList.add("赵敏");
nameList.add("鑫磊");
nameList.add("吴莫愁");
nameList.add("张天爱");
nameList.add("张广仁仁");
long count = nameList.stream().skip(2).count();
-
组合:concat
如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concatStream<String> s1 = Stream.of("鑫磊"); Stream<String> s2 = Stream.of("小冰"); Stream<String> s3 = Stream.concat(s1, s2); s3.forEach((name)‐>System.out.println(name));
Stream的综合案例
- 需求
-
第一个队伍只要名字为三个字的成员名字;
-
第一个队伍筛选之后只要前三个;
-
第二队只要姓张的成员姓名;
-
第二个队伍筛选之后不要前2个人;
-
将两个队伍合并为一个队伍;
-
打印整个队伍的Person对象信息
List<String> nameList = new ArrayList<>(); nameList.add("张芷若"); nameList.add("赵敏"); nameList.add("鑫磊"); nameList.add("吴莫愁"); nameList.add("张天爱"); nameList.add("张广仁仁"); nameList.add("田宝路"); nameList.add("万里"); nameList.add("逍遥"); nameList.add("想静静"); nameList.add("张无忌"); nameList.add("张三丰"); // a. 第一个队伍只要名字为3个字的成员姓名; // b. 第一个队伍筛选之后只要前3个人; Stream<String> stream1 = nameList.stream().filter(name -> name.length() == 3).limit(3); // c. 第二个队伍只要姓张的成员姓名; // d. 第二个队伍筛选之后不要前2个人; Stream<String> stream2 = nameList.stream().filter(name -> name.startsWith("张")).skip(2); // e. 将两个队伍合并为一个队伍; // f. 打印整个队伍的Person对象信息 Stream.concat( nameList.stream().filter(name -> name.length() == 3).limit(3), nameList.stream().filter(name -> name.startsWith("张")).skip(2)) .forEach(name -> System.out.println(name));
-