说到Stream容易想到 IO Stream,而流不一定是IO流。在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库有的弊端。
需求分析:
普通操作:
public class PrefaceNormal {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("张杰");
arrayList.add("张还行");
arrayList.add("王小");
arrayList.add("张怡");
arrayList.add("唐山");
// 筛选姓张的
ArrayList<String> listA = new ArrayList<>();
for(String str: arrayList){
if(str.startsWith("张")){
listA.add(str);
}
}
// 筛选3个字的
ArrayList<String> listB = new ArrayList<>();
for(String str: arrayList){
if(str.length() == 3){
listB.add(str);
}
}
// 遍历结果
for (String str : listB){
System.out.println(str); // 输出:张还行
}
}
}
可见用了很多次增强for循环,代码量很多,很冗杂,不美观,效率低。
Stream操作:
public class PrefaceStream {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("张杰");
arrayList.add("张还行");
arrayList.add("王小");
arrayList.add("张怡");
arrayList.add("唐山");
arrayList.stream()
// Lambda实现Predicate接口中的test方法,返回Boolean值
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
// Lambda实现Customer接口中的accept方法
.forEach(name -> System.out.println(name));
// 不对原有的集合内容做改变,只是对符合要求的数据进行筛选
}
}
这里的filter
、map
、skip
都是对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法count
执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。
备注:Stream流 其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身不存储任何元素(或地址值)
Stream是一个来数据源的元素队列
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张还行", "heroC", "yikeX");
stream.filter(name -> name.startsWith("张"))
.forEach(name -> System.out.println(name));
// stream使用了一次了,再使用一次,会报IllegalStateException异常
// 说明之前的流已经关闭,不能再使用了。
stream.forEach(name -> System.out.println(name));
}
}
Stream操作两个基础的特征:
当使用一个流的时候,通常包括三个基本步骤:获取一个数据源 → 数据转换 → 执行操作获取想要的结果,每次转换原有Stream对象不改变,返回一个新的Stream对象,这就允许对其操作可以像链条一样排列,变成一个管道。
java.util.stream Interface Stream
接口
获取流的方式:
Collection
集合都可以通过stream
默认方法获取流stream
的静态方法of
可以获取对应的流,of方法是可变参数,可变参数底层就一数组public class CollectionStream {
public static void main(String[] args) {
// List集合获取流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// Set集合获取流
Set<String> set = new HashSet<>();
Stream<String> stream1 = set.stream();
// 键获取流
Map<String,String> map1 = new HashMap<>();
Collection<String> collKey = map1.keySet();
Stream<String> stream2 = collKey.stream();
// 值获取流
Map<String,String> map2 = new HashMap<>();
Collection<String> collValue = map2.values();
Stream<String> stream3 = collValue.stream();
// 键值对获取流
Map<String,String> map3 = new HashMap<>();
Collection<Map.Entry<String, String>> entries = map3.entrySet();
Stream<Map.Entry<String, String>> stream4 = entries.stream();
// 流的静态方法of,可变参数获取流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
// 流的静态方法of,数组获取流
Integer[] num = {1,2,3,4};
Stream<Integer> num1 = Stream.of(num);
String[] str = {"a","b"};
Stream<String> str1 = Stream.of(str);
}
}
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以分为两种:
count
和forEach
方法,更多见API文档。forEach与增强for是不同的
void forEach(Consumer super T> action)
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张还行", "heroC", "yikeX");
stream.forEach(name -> System.out.println(name)); // 对流中的每一个数据进行处理
}
}
可通过filter将一个流转换成另一个流
Stream
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张还行", "heroC", "yikeX");
stream.filter(name -> name.startsWith("张")) // 可以将流过滤转换成另一个只符合要求的流
.forEach(name -> System.out.println(name)); // 对新的流每个数据进行遍历处理
}
}
// 输出:张还行
将流中的方法映射到另一个流中
将T类型
的数据转换成R类型
的数据就是“映射”。
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "2", "3", "4");
Stream<Integer> stream1 = stream.map(str -> Integer.parseInt(str));
stream1.forEach(num -> System.out.print(num+" "));
}
}
// 输出:1 2 3 4
long count()
返回long类型数据,统计流中元素的个数
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "12", "23", "1234");
long count = stream.filter(str -> str.startsWith("1")).count();
System.out.println(count);
}
}
// 输出:3
limit方法可以对流中的数据进行截取,只取用前n个。
Stream
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "12", "23", "1234");
stream.limit(3).forEach(str -> System.out.print(str+" "));
}
}
// 输出:1 12 23
如果流的当前长度大于n,则跳过前n个元素;否则将会得到一个长度为0的空流。
Stream
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "12", "23", "1234");
stream.skip(2).forEach(str-> System.out.print(str+" "));
}
}
// 输出:23 1234
将有两个流,合并成一个流
static
备注:这是一个静态方法,与
java.lang.String
当中的concat
方法是不同的。
public class PrefaceStream {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("heroC");
Stream<String> stream2 = Stream.of("yikeX");
Stream.concat(stream1,stream2).forEach(str -> System.out.print(str+" "));
}
}
// 输出:heroC yikeX
题目:
现在有两个ArrayList集合存储队伍当中的多个成员姓名,使用Stream流完成以下操作:
Person
对象;存储到一个新集合中。Person
对象信息。public class StreamExample {
public static void main(String[] args) {
ArrayList<String> one = new ArrayList<>();
one.add("张还行");
one.add("张还");
one.add("张行");
one.add("施柏宇");
one.add("林俊杰");
one.add("张一山");
// 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
Collection<String> nameThree = new ArrayList<>();
one.stream().filter(name -> name.length()==3).forEach(name -> nameThree.add(name));
// 2. 第一个队伍筛选后只要前3个人名;存储到一个新集合中。
Collection<String> nameThird = new ArrayList<>();
nameThree.stream().limit(3).forEach(name -> nameThird.add(name));
ArrayList<String> two = new ArrayList<>();
two.add("张一山");
two.add("黄轩");
two.add("张行");
two.add("张若昀");
two.add("罗晋");
two.add("张子枫");
// 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
Collection<String> nameZhang = new ArrayList<>();
two.stream().filter(name -> name.startsWith("张")).forEach(name -> nameZhang.add(name));
// 4. 第二个队伍筛选之后不要前2个人名;存储到一个新集合中。
Collection<String> nameSkip = new ArrayList<>();
nameZhang.stream().skip(2).forEach(name -> nameSkip.add(name));
// 5. 将两个筛选后的队伍合并为一个队伍;存储到一个新集合中。
Collection<String> nameConcat = new ArrayList<>();
Stream.concat(nameThird.stream(), nameSkip.stream()).forEach(name -> nameConcat.add(name));
// 6. 根据姓名创建`Person`对象;存储到一个新集合中。
Collection<Person> namePerson = new ArrayList<>();
nameConcat.stream().forEach(name -> namePerson.add(new Person(name)));
// 7. 打印整个队伍的`Person`对象信息。
long count = namePerson.stream().count();
System.out.println("筛选后的队伍总人数为:"+count);
namePerson.stream().forEach(namePer -> System.out.println(namePer));
}
}
输出:
筛选后的队伍总人数为:5
Person{name='张还行'}
Person{name='施柏宇'}
Person{name='林俊杰'}
Person{name='张若昀'}
Person{name='张子枫'}