Stream流
Java8的两个重大改变,一个是Lambda表达式,另一个就是本Stream API表达式。
Stream是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作。
1. 为什么要使用Sream流
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:
一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
需求:
- 拿到所有姓张的。
- 拿到名字长度为3个字的。
- 打印这些数据。
传统的写法是:
public class Test01 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
// 1. 拿到所以姓张的
ArrayList<String> zhangList = new ArrayList<>();
for (String name : list) {
if (name.startsWith("张")){
zhangList.add(name);
}
}
// 2. 拿到名字长度为3个字的
ArrayList<String> threeList = new ArrayList<>();
for (String name : zhangList) {
if (name.length()==3){
threeList.add(name);
}
}
// 3. 打印这些数据
for (String name : threeList) {
System.out.println(name);
}
}
}
分析:
这段代码中含有三个循环,每一个作用不同:
- 首先筛选所有姓张的人。
- 然后筛选名字有三个字的人。
- 最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。
那Stream能否给我们带来怎样更加优雅的写法呢?
使用Stream流的写法:
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
// 使用stream流
list.stream()
.filter(item->item.startsWith("张"))
.filter(item->item.length()==3)
.forEach(item-> System.out.println(item));
}
我们发现这种写法非常的简短。
stream流对集合的操作语法简介,性能比传统快。
2. Stream流的原理
**注意:**stream和I0流(lnputStream/0utputStream)没有任何关系。
Stream流式思想类似于工厂车间的"生产流水线",Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工席。在流水线上,通过多个工席让一个原材料加工成一个商品。原料—>工序1—>工序2—>工序n—>产品
3. 如何获取Stream流对象
有四种方式:
- 通过Collection对象的stream()或parallelStream()方法。
- 通过Arrays类的stream()方法。
- 通过Stream接口的of()、iterate()、generate()方法。
- 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法
public class Test02 {
public static void main(String[] args) {
//通过集合对象调用stream()获取流
List<String> list=new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
Stream<String> stream =list.stream();
//通过Arrays数组工具类获取stream对象
int[] arr={3,4,5 ,63,2,34};
IntStream stream1= Arrays.stream(arr);
//使用stream类中of方法
Stream<String> stream2 = Stream.of("hello","world","spring","java");
//LongStream
LongStream range = LongStream.range(1,10);
//上面都是获取的串行流。还可以获取并行流。如果流中的数据量足够大,并行流可以加快处速度。
Stream<String> stringStream = list.parallelStream();
// 可以多线程处理的Stream流
// stringStream.forEach(item->System.out.println(item));
// 可以用引用
stringStream.forEach(System.out::println);
}
}
4. Stream的常用api方法
中间操作api:一个操作的中间链,对数据源的数据进行操作。而这种操作的返回类型还是一个Stream对象。
终止操作api:一个终止操作,执行中间操作链,并产生结果,返回类型不在是stream流对象。
4.1 filter
filter(): 用于过滤 Stream 中的元素,参数是一个 Predicate 函数式接口,用于定义过滤条件。
public class Test03 {
public static void main(String[]args){
List<Person> personList =new ArrayList<>();
personList.add(new Person("萧炎",18,"中国","男"));
personList.add(new Person("Tom", 24,"美国", "男"));
personList.add(new Person("Harley",22,"英国","女"));
personList.add(new Person("美杜莎",20,"中国","女"));
personList.add(new Person("韩立",22,"中国","男"));
personList.add(new Person("南宫婉",21,"中国","女"));
personList.add(new Person("罗峰",20,"中国","男"));
personList.add(new Person("石昊",22,"中国","男"));
// 找到年龄大于18岁的人并输出;filter()过滤器需要一个断言接口函数,断言接口返回true,获取该元素foreach(consumer)
// 无论执行多少个中间操作,如果没有执行终止作,那么中间作都不会被执行。
personList.stream()
.filter(item->item.getAge()>18)
.forEach(System.out::println);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Person{
private String name;
private int age;
private String country;
private String sex;
}
运行结果:
4.2 count
返回 Stream 中的元素个数。
// 找出所有中国人的数量。--->filter过滤掉其他国家的人 count()终止操作
Long count = personList.stream().
filter(item -> item.getCountry().equals("中国")).count();
System.out.println(count);
运行结果:
4.3 map
map–接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
//集合中每个元素只要名.map--->原来流中每个元素转换为另一种格式。
personList.stream()
.map(item->{
Map<String,Object> m=new HashMap<>();
m.put("name",item.getName());
m.put("age",item.getAge());
return m;
})
.forEach(System.out::println);
运行结果:
4.4 sorted
对 Stream 中的元素进行排序,默认是自然排序(升序)。
//对流中元素排序
personList.stream()
.sorted((o1,o2)->o1.getAge()-o2.getAge())
.forEach(System.out::println);
运行结果:
4.5 max
使用
max()
方法来获取 Stream 中的最大值。这个方法可以应用于不同类型的 Stream,如Stream<Integer>
、Stream<Double>
或者自定义对象类型的 Stream,只要你提供了适当的比较器(Comparator)。
// 查找最大年龄的人。max终止操作
Optional<Person> max = personList.stream().max((o1,o2)->o1.getAge()- o2.getAge());
System.out.println(max.get());
运行结果:
4.6 reduce
对 Stream 中的元素进行归约操作,得到一个结果。参数是一个 BinaryOperator 函数式接口,用于定义归约规则。
//求集合中所有人的年龄和。参数和返回类型必须一致
Optional<Integer> reduce = personList.stream()
.map(item -> item.getAge())
.reduce((a,b)->a + b);
System.out.println(reduce.get());
//求集合中所有人的年龄和再加10。第一个参数为初始值
Integer reduce1 = personList.stream()
.map(item -> item.getAge())
.reduce(10, (a, b) -> a + b);
System.out.println(reduce1);
运行结果:
4.7 collect
将 Stream 转换为其他形式,如 List、Set、Map 等。参数是一个 Collectors 类中提供的收集器。
//搜集方法 collect 它属于终止方法
// 年龄大于20日性别为女的
List<Person>collect=personList.stream().filter(item->item.getAge()>20)
.filter(item->item.getSex().equals("女"))
.collect(Collectors.toList());
System.out.println(collect);
运行结果:
4.8 allMatch、amyMatch
allMatch:检查 Stream 中的所有元素是否都符合给定的条件。参数是一个 Predicate 函数式接口。
anyMatch:检查 Stream 中是否至少有一个元素符合给定的条件。参数是一个 Predicate 函数式接口。
// 性别为女,年龄都大于20才为真
// findFirst match
/*Optional<Person> first = personList.parallelStream()
.filter(item -> item.getSex().equals("女"))
.findAny();*/
boolean b = personList.parallelStream()
.filter(item -> item.getSex().equals("女"))
.allMatch(item -> item.getAge()>= 20);
System.out.println(b);
运行结果: