传统集合的多步遍历代码
几乎所有的集合(如Collection接口或Map接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。
循环遍历的弊端
Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行了对比说明。现在,我们仔细体会一下上例代码,可以发现:
for循环的语法就是“怎么做”for循环的循环体才是“做什么”为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?
遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环。前者是目的,后者是方式。
试想一下,如果希望对集合中的元素进行筛选过滤:
- 将集合A根据条件一过滤为子集B;
- 然后再根据条件二过滤为子集C。
例如:
package test.day28Stream;
import java.util.ArrayList;
import java.util.List;
public class Demo01 {
//普通语法输出
public static void method() {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("张三丰");
list.add("金毛狮王");
list.add("周芷若");
System.out.println(list);
List<String> listA = new ArrayList<>();
for (String s : list) {
if (s.startsWith("张")) {
listA.add(s);
}
}
System.out.println(listA);
List<String> listB = new ArrayList<>();
//只要姓名长度为3的人,再次存储到一个新的集合
for (String s : listA) {
if (s.length() == 3) {
listB.add(s);
}
}
System.out.println(listB);
}
//使用Stream流实现
public static void method1() {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("张三丰");
list.add("金毛狮王");
list.add("周芷若");
System.out.println(list);
}
public static void main(String[] args) {
method();
}
}
这段代码中含有三个循环,每一个作用不同:
- 首先筛选所有姓张的人;
- 然后筛选名字有三个字的人;
- 最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。那,Lambda的衍生物Stream能给我们带来怎样更加优雅的写法呢?
Stream流的更优代码
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("张三丰");
list.add("金毛狮王");
list.add("周芷若");
System.out.println(list);
//对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
//对list集合进行过滤,只要姓张的且长度为3的人,就存储到一个新的集合中
list.stream().filter(name->name.startsWith("张"))
.filter(name->name.length() == 3)
.forEach(name-> System.out.println(name));
}
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。代码中并没有体现使用线性循环或是其他任何算法进行遍历,我们真正要做的事情内容被更好地体现在代码中。
获取流的方法
java.util.stream.Stream
所有的Collection集合都可以通过stream默认方法获取流;
Stream接口的静态方法of可以获取数组对应的流。
package test.day28Stream;
import java.util.*;
import java.util.stream.Stream;
/*
java.util.stream.Stream<T>
所有的Collection集合都可以通过stream默认方法获取流;
Stream接口的静态方法of可以获取数组对应的流。
*/
public class Demo02GetStream {
public static void main(String[] args) {
//把集合转化为Stream流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream1 = set.stream();
Map<String, String> map = new HashMap<>();
//获取键
Set<String> set1 = map.keySet();
Stream<String> stream2 = set1.stream();
//获取值
Collection<String> values = map.values();
Stream<String> stream3 = values.stream();
//获取键值对
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream4 = entries.stream();
//把数组转换为Stream流
Stream<Integer> integerStream = Stream.of( 1, 2, 3 );
//可变参数可以传递数组
int[] arr = { 1, 2, 3 };
Stream<int[]> arr1 = Stream.of(arr);
String[] arr2 = { "a", "b", "c" };
Stream<String> arr21 = Stream.of(arr2);
}
}
常用方法
1、forEach(Consumer):用来遍历流中的数据
是一个终结方法,遍历之后就不能继续调用Stream流中的其它方法。
由于Consumer是一个函数式接口,可以传递Lambda表达式,作用:消费数据
package test.day28Stream;
import java.util.stream.Stream;
public class Demo03ForEach {
public static void main(String[] args) {
//获取Stream流
Stream<String> stream = Stream.of("张三", "李四", "王五");
//调用方法遍历数据
/*stream.forEach((String name) -> {
System.out.println(name);
});*/
//Lambda表达式可优化
stream.forEach(name -> System.out.println(name));
}
}
2、filter(Predicate)
由于Predicate是一个函数式接口,作用:对某种类型的数据进行判断
package test.day28Stream;
import java.util.stream.Stream;
public class Demo04Filter {
public static void main(String[] args) {
//获取Stream流
Stream<String> stream = Stream.of("张三", "李四", "王五");
//使用方法过滤
Stream<String> str = stream.filter((String name) -> {
return name.startsWith("张");
});
System.out.println(str); //java.util.stream.ReferencePipeline$2@2d98a335
str.forEach(name -> System.out.println(name)); //张三
}
}