1.为什么使用Stream流
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:
一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
public class My {
public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
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 class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰","何线程");
list.stream()
.filter(item->item.startsWith("张"))
.filter(item->item.length()==3)
.forEach(item-> System.out.println(item));
}
}
对集合的操作语法简洁:性能比传统快。
2.Stream流的原理
注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
3.如何获取Stream流
1.通过Collection对象的Stream()或parallelStream()方法
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
//通过集合对象来调用Stream串行流
Stream<String> stream = list.stream();
//通过集合对象来调用Stream并行流
Stream<String> stringStream = list.parallelStream();
2.通过Arrys类的Stream()方法
//通过Arrays的Stream()方法来调用
int[] arr = {1,2,1,3,4,5,6};
IntStream stream1 = Arrays.stream(arr);
3.通过Stream接口的of(),iterate(),generate()方法
//通过Stream的of()来调用
Stream<String> list1 = Stream.of("张无忌", "周芷若", "赵敏", "张强", "张三丰");
4.通过IntStream、LongStream、DoubleStream接口中的of,range,rangeClosed方法
LongStream longStream = LongStream.of(1, 2, 3, 4, 5, 8, 6, 8);
//通过LongStream.range 来获取 1-10 的Stream流对象但不包含10,
LongStream range = LongStream.range(1, 10);
//通过LongStream.rangeClosed 来g获取 1-10 的Stream流对象 包含10,
LongStream longStream1 = LongStream.rangeClosed(1, 10);
4. Stream流中常见的api
中间操作api: 一个操作的中间链,对数据源的数据进行操作。而这种操作的返回类型还是一个Stream对象。
终止操作api: 一个终止操作,执行中间操作链,并产生结果,返回类型不在是Stream流对象。
filter 筛选 / foreach 遍历 / count 计数
public class Test02 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));
//1.找出年龄大于18的人并输出;filter()过滤器需要一个断言接口函数
//无论执行多少个中间操作 如果没有终止草错那么中间操作都不会被执行
personList.stream().filter(item->item.getAge()>18).forEach(System.out::println);
// 2.找出所有中国人的数量 count 计数终止操作
long count = personList.stream().filter(item -> item.getCountry() == "中国").count();
System.out.println(count);
}
}
class Person {
private String name;
private Integer age;
private String country;
private char sex;
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", country='" + country + '\'' +
", sex=" + sex +
'}';
}
}
map | sorted 排序
map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
//3.只要每个对象中的名字 map 原来流中的每一个元素转换成另一个格式
personList.stream().map(item->item.getAge()).forEach(System.out::println);
//4
personList.stream().map(item->{
Map map = new HashMap();
map.put("name",item.getName());
map.put("age",item.getAge());
return map;
}).forEach(System.out::println);
//将流中的元素排序
personList.stream().sorted((o1, o2) -> o1.getAge().compareTo(o2.getAge())).forEach(System.out::println);
min 最小 / max 最大 /distinct 去重
//6.查找年龄最大的人 max是终止操作
Optional<Person> max = personList.stream().max((o1, o2) -> o1.getAge().compareTo(o2.getAge()));
System.out.println("年龄最大的人"+max.get());
//7.查找年龄最小的人
Optional<Person> min = personList.stream().min((o1, o2) -> o1.getAge().compareTo(o2.getAge()));
System.out.println("年龄最小的人"+min.get());
// 查看都有那个国家的人
personList.stream().map(item->item.getCountry()).distinct().forEach(System.out::println);
reduce/规约
//8.求集合中所有人的年龄和
//reduce 规约把一流缩减成一个值,能实现对集合就和,求乘积和求最值的操作 reduce(a, b)可以不写 10 10代表这a的初始值为10
Optional<Integer> reduce = personList.stream().map(item -> item.getAge()).reduce((a, b) -> a + b);
System.out.println(reduce.get());
collect /搜集 match find
//collect 收集方法
//9 年龄大于20的且性别为男
List<Person> collect = personList.stream()
.filter(item -> item.getAge() > 20)
.filter(item -> item.getSex() == 'M')
.collect(Collectors.toList());
System.out.println(collect);
//find匹配 match查找
Optional<Person> first = personList.stream().findFirst();
System.out.println(first);
// findFirst 获取第一个 findand 随机获取一个
Optional<Person> any = personList.stream().parallel().findAny();
// match 所有元素满足条件返回true 否则返回true anyMatch 只要有一个元素满足条件就返回true noneMatch 没有满足条件的返回true
boolean b = personList.stream().parallel().allMatch(item -> item.getAge() > 20);
boolean c = personList.stream().parallel().anyMatch(item -> item.getAge() > 20);
System.out.println(b);
System.out.println(c);