说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带
来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。
循环遍历的弊端
Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行
了对比说明。现在,我们仔细体会一下上例代码,可以发现
- for循环的语法就是“怎么做”
- for循环的循环体才是“做什么”
为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从
第一个到最后一个顺次处理的循环。前者是目的,后者是方式。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循
环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。
public class Main {
public static void main(String[] args) {
//创建一个list集合储存姓名
List<String> list=new ArrayList<>();
list.add("小刘");
list.add("小赵");
list.add("小宝贝");
list.add("老王");
list.add("老王八");
//过滤以“小”开头为的字符
list.stream().filter(s->s.startsWith("小"))
//过滤字符长度为3的
.filter(s->s.length()==3)
//遍历stream集合
.forEach(s-> System.out.println(s));
}
}
打印结果:小宝贝
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤以"小"开头、过滤长度为3、逐一打印。代码
中并没有体现使用线性循环或是其他任何算法进行遍历,我们真正要做的事情内容被更好地体现在代码中。
stream的常用方法
- 逐一处理:forEach
虽然方法名字叫 forEach ,但是与for循环中的“for-each”昵称不同。
public class Main {
public static void main(String[] args) {
//获取一个stream流
Stream<String> stream=Stream.of("小张","老王","小刘","小花");
//使用stream流中的方法foreach对stream流中的数据进行遍历
stream.forEach(s-> System.out.println(s));
打印结果:
小张
老王
小刘
小花
- 过滤:filter
可以通过 filter 方法将一个流转换成另一个子集流。
public class Main {
public static void main(String[] args) {
//创建一个stream流
Stream<String> stream = Stream.of("萧炎", "萧熏儿", "萧战", "药老", "美杜莎");
//对stream流中的元素过滤,只要姓萧的人
Stream<String> stream1 = stream.filter(s -> s.startsWith("萧"));
//遍历stream1流
stream1.forEach(s-> System.out.println(s));
}
}
打印结果:
萧炎
萧熏儿
萧战
在这里通过Lambda表达式来指定了筛选的条件:必须姓张。
-
Stream流的特点:只能使用一次
Stream流属于管道留,只能被消费(使用)一次,第一个Stream流调用完毕方法,数据就会流转到下一个Stream上,而这是第一个stream流已经使用完毕就会关闭。所以第一个stream流就不能调用方法了 -
映射:map
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。
public class Main {
public static void main(String[] args) {
//获取一个String类型的stream流
Stream<String> stream = Stream.of("1", "2", "3", "4", "5");
//使用map方法,把字符串类型的整数,转换(映射)成Integer类型的整数
Stream<Integer> stream1= stream.map((String s) -> {
return Integer.parseInt(s);});
//遍历stream1
stream1.forEach(s-> System.out.println(s));
}
}
打印结果:
1
2
3
4
5
这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对象)。
- 统计个数:count
count用于统计Stream流中元素的个数
long count();
count方法是一个终结方法,返回值是一个long类型的整数,所以不能继续调用Stream流中的其他方法
public class Main {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("1");
list.add("123");
list.add("145");
list.add("481");
list.add("691");
Stream<String> stream = list.stream();
long count = stream.count();
System.out.println(count);
}
}
打印结果:
5
- 取用前几个:limit
limit 方法可以对流进行截取,只取用前n个
public class Main {
public static void main(String[] args) {
// 获取stream流
Stream<String> stream = Stream.of("张三", "李斯", "王五", "赵六", "田七");
Stream<Stream<String>> stream1 = Stream.of(stream);
//使用limit对Siream流中的元素进行截取,只要前三个元素
Stream<String> stream2 = stream.limit(3);
//遍历stream2
stream2.forEach(s-> System.out.println(s));
}
}
打印结果:
张三
李斯
王五
- 跳过前几个:skip
如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流
public class Main {
public static void main(String[] args) {
// 获取stream流
Stream<String> stream = Stream.of("张三", "李斯", "王五", "赵六", "田七");
Stream<Stream<String>> stream1 = Stream.of(stream);
//使用skip方法,跳过前三个元素
Stream<String> stream2 = stream.skip(3);
//遍历
stream2.forEach(s-> System.out.println(s));
}
}
打印结果:
赵六
田七
- 组合:concat
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat
public class Main {
public static void main(String[] args) {
// 获取stream流
Stream<String> stream1 = Stream.of("张三", "李斯", "王五", "赵六", "田七");
// 获取stream流
Stream<String> stream2 = Stream.of("萧炎", "萧熏儿", "萧战", "药老", "美杜莎");
//把两个流组合成一个流
Stream<String> concat = Stream.concat(stream1, stream2);
//遍历
concat.forEach(s-> System.out.println(s));
}
}
打印结果:
张三
李斯
王五
赵六
田七
萧炎
萧熏儿
萧战
药老
美杜莎