Java中的Stream流


内容概要

  • 掌握流的使用
  • 掌握流的几个重要中间操作。(filter,map)
  • 掌握流的几个重要终止操作(collect)
  • 会使用流进行数据的处理工作

1 引言

@Data
public class Student {
    // 常量。 
    public enum Address {
        BJ, SH, WH, SZ
    }

    public Student(String name, int age, int height, Address address) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.address = address;
    }

    private String name;
    private int age;
    private int height;
    private Address address;
  
}
public class StudentList {
  public List<Student> studentList;

  public StudentList() {
    this.studentList = new ArrayList<>();

    studentList.add(new Student("aa", 18, 170, Student.Address.BJ));
    studentList.add(new Student("bb", 20, 163, Student.Address.SH));
    studentList.add(new Student("cc", 30, 182, Student.Address.WH));
    studentList.add(new Student("dd", 16, 190, Student.Address.BJ));
    studentList.add(new Student("ee", 15, 210, Student.Address.SH));
    studentList.add(new Student("ff", 17, 160, Student.Address.WH));
    studentList.add(new Student("gg", 18, 169, Student.Address.BJ));
    studentList.add(new Student("hh", 20, 173, Student.Address.WH));
    studentList.add(new Student("ii", 22, 192, Student.Address.SH));
    studentList.add(new Student("jj", 25, 172, Student.Address.SH));
    studentList.add(new Student("kk", 24, 188, Student.Address.BJ));
    // 重复元素 kk
    // studentList.add(new Student("kk", 24, 188, Student.Address.BJ));
    studentList.add(new Student("ll", 17, 161, Student.Address.WH));
    studentList.add(new Student("mm", 18, 169, Student.Address.SH));
    studentList.add(new Student("nn", 20, 162, Student.Address.BJ));
    studentList.add(new Student("oo", 22, 166, Student.Address.SH));
    studentList.add(new Student("pp", 24, 176, Student.Address.WH));
    studentList.add(new Student("qq", 22, 173, Student.Address.BJ));
    // 重复元素 qq
    // studentList.add(new Student("qq", 22, 173, Student.Address.BJ));
    studentList.add(new Student("rr", 24, 177, Student.Address.BJ));
    studentList.add(new Student("ss", 17, 169, Student.Address.SH));
    studentList.add(new Student("tt", 18, 170, Student.Address.SH));
    studentList.add(new Student("uu", 20, 171, Student.Address.WH));
    studentList.add(new Student("vv", 22, 172, Student.Address.WH));
    studentList.add(new Student("ww", 24, 181, Student.Address.BJ));
    studentList.add(new Student("xx", 18, 188, Student.Address.SH));
    studentList.add(new Student("yy", 20, 183, Student.Address.BJ));
    studentList.add(new Student("zz", 22, 191, Student.Address.WH));
  }
}

我们要对这个学生列表进行处理: 得到来自北京同学, 并且高度最高的三个同学

应该怎么做?

public static void main(String[] args) {
    StudentList studentList = new StudentList();
    List<Student> students = studentList.studentList;

  TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {

      int com = o2.getHeight() - o1.getHeight();

      if (com != 0) {
        return com;
      }

      com = com == 0 ? o2.getName().compareTo(o1.getName()) : com;
      com = com == 0 ? o2.getAge() - o1.getAge() : com;
      com = com == 0 ? o2.getAddress().compareTo(o1.getAddress()) : com;

      return com;
    }
  });


    for (Student student : students) {
      if (student.getAddress() == Student.Address.BJ) {
        studentTreeSet.add(student);
      }
    }

    System.out.println(studentTreeSet.first());
    studentTreeSet.pollFirst();
    System.out.println(studentTreeSet.first());
    studentTreeSet.pollFirst();
    System.out.println(studentTreeSet.first());
    studentTreeSet.pollFirst();
  }

使用流解决上述问题

public void testWithStream() {
    List<Student> studentList = new StudentList().studentList;

    List<Student> students = studentList.stream().filter(p -> p.getAddress() == Student.Address.BJ).sorted(new Comparator<Student>() {
      @Override
      public int compare(Student s1, Student s2) {
        return s2.getHeight() - s1.getHeight();
      }
    }).limit(3).collect(Collectors.toList());

    System.out.println(students);
  }

2 流的概述

2.2 什么是流

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

通俗的讲: 也就是说, Stream流是Java在JDK1.8提供的对集合数据进行优化/简化操作的一种数据处理方式。

2.3 流用来解决什么问题

Stream流一般用来处理Java中的集合类数据, 进以避免在日常代码书写中的对集合数据操作的性能以及代码冗长问题。

2.4 怎么使用流

使用一个流的时候,通常包括三个基本步:
1. 一个数据源, 创建一个流

2. 多个/0个 中间操作, 形成一条流水线

3. 一个终止/终端操作, 执行流水线,并生成结果

3 创建流

1. 调用集合类的stream方法,生成一个流(最常用/常见)

Collection collection = new ArrayList();
Stream stream = collection.stream();
  1. 由值创建
Stream<String> zs = Stream.of("zs", "ls", "wu", "zl");

3.由数组创建

String [] strs = {"zs", "ls", "wu"};
Stream<String> stream = Arrays.stream(strs);

4 中间操作

两个简单的中止操作:

count(): 流里面有多少元素,最终会返回Long类型。

collect(Collectors.toList()): 将流里面的元素,存储为一个List;

4.1 filter(过滤操作)

filter方法用于通过设置的条件过滤出元素。

使用场景:

  • 对元素进行过滤。比如想要某个地区的学生,比如想要年龄大于20的学生。
List<Student> studentList = new StudentList().studentList;
// 获取所有北京地区的同学
List<Student> collect = studentList.stream()
  .filter(s -> s.getAddress().equals(Student.Address.BJ))
  .collect(Collectors.toList());
System.out.println(collect);

注意: 每次中间操作会返回一个 Stream (可以有多次中间操作),这就允许对其操作可以像链条一样排列,变成一个管道。

// Stream<T> filter(Predicate<? super T> predicate); 
// public interface Predicate{}

// filter方法参数----  需要传入的是一个 Predicate类型的实例。
// Predicate 里面只有一个方法。输入参数根据流里的数据确定,输出参数是个boolean

4.2 distinct(去重)

distinct方法用于筛选元素(相当于去除重复元素)

使用场景:

  • 对元素进行去重处理。底层是LinkedHashSet

所以需要注意:如果使用自定义的类,调用distinct,需要重写hashCode和equals方法

//  Stream<T> distinct();
//  distinct方法----筛选元素, 筛选的机制是根据元素的hashCode和equals判断重复
List<Student> studentList = new StudentList().studentList;

// 北京的人
long beijingCount = studentList.stream()
  // 过滤 只取出北京的
  .filter(s -> Student.Address.BJ.equals(s.getAddress())).count();

// 去重后北京的人
long distinctBeijingCount = studentList.stream()
  .filter(s -> Student.Address.BJ.equals(s.getAddress())).distinct().count();

System.out.println(beijingCount);
System.out.println(distinctBeijingCount);

4.3 limit(截取)

limit 方法用于获取指定数量(最大)的流。

// Stream<T> limit(long maxSize);
// limit(n)方法, 返回前n个元素.
// 如果流中有10个元素,limit(3) 则只返回3个元素。
// 如果流中有1个元素,limit(3) 则只返回1个元素。

    List<Student> studentList = new StudentList().studentList;

    // 获取身高高于210的
    List<Student> superHighPeople = studentList.stream().filter(s -> s.getHeight() > 210)
        .limit(3).collect(Collectors.toList());
    System.out.println(superHighPeople);

    // 获取身高高于180的
    List<Student> highPeople = studentList.stream().filter(s -> s.getHeight() > 180)
        .limit(3).collect(Collectors.toList());
    System.out.println(highPeople);

    // 获取高于160的
    List<Student> normalPeople = studentList.stream().filter(s -> s.getHeight() > 160)
        .limit(3).collect(Collectors.toList());

    System.out.println(normalPeople);

4.4 skip(跳过)

skip(n)方法, 跳过前n个元素

    List<Person> personList = StudentList.personList;
// Stream<T> skip(long n);
// skip(n)方法, 跳过前n个元素, 返回之后的元素.  (如果整体不够n个, 返回空流)

    List<Student> studentList = new StudentList().studentList;

    // 总共五个元素
    List<Student> collect1 = studentList.stream().filter(s -> s.getAge() > 22 && s.getAge() < 25)
        .collect(Collectors.toList());

    System.out.println(collect1);

    // 跳过1个元素,只剩下4个
    List<Student> collect2 = studentList.stream().filter(s -> s.getAge() > 22 && s.getAge() < 25)
        .skip(1).collect(Collectors.toList());
    System.out.println(collect2);

4.5 map(转换)

map 方法用于映射每个元素到对应的结果。

从一个类型转换到另外一种类型。或者不变化类型

使用场景:

  • 输入的是一个类型,希望以另外一个类型输出。
    • 比如我想将一批人的年龄,转化为 青年(0-29),中青年(30-39),中年(40-49),中老年(50岁以上)。
    • 比如我想将身高,映射为 超高人,高人,普通人。[190, +无穷), [175, 190) , (-无穷, 175]
// <R> Stream<R> map(Function<? super T, ? extends R> mapper);
// TODO: map映射返回新的数据,  map的参数是一个方法
        
	// 获取所有学生姓名
    List<Student> studentList = new StudentList().studentList;

    List<String> collect = studentList.stream().map(student -> student.getName())
        .collect(Collectors.toList());
    System.out.println(collect);
	// 获取所有学生姓名的首字母
    List<String> collect1 = studentList.stream().map(s -> s.getName().substring(0, 1))
        .collect(Collectors.toList());
    System.out.println(collect1);
	// 获取非常高的学生(超过190) 返回高人这个类
    List<SuperPerson> collect2 = studentList.stream().filter(s -> s.getHeight() > 190)
        .map(s -> new SuperPerson(s.getName(), s.getHeight()))
        .collect(Collectors.toList());
    System.out.println(collect2);

class SuperPerson{
    String name;
    int height;
}
// 只需要人是什么类型的  
// 超高人,高人,普通人
// [190, +无穷), [175, 190) , (-无穷, 175]

List<Student> studentList = new StudentList().studentList;
List<String> collect = studentList.stream()
  .filter(s -> Student.Address.BJ.equals(s.getAddress())).map(s -> {
  int height = s.getHeight();

  if (height >= 190) {
    return "超高人";
  } else if (height >= 175) {
    return "高人";
  } else {
    return "普通人";
  }
}).collect(Collectors.toList());
System.out.println(collect);

4.6 sorted(排序)

sorted 方法用于对流进行排序

//     Stream<T> sorted();: 自然顺序排序
//     Stream<T> sorted(Comparator<? super T> comparator);: 提供一个比较器

	// 对高于180的同学根据身高进行排序
    List<Student> studentList = new StudentList().studentList;
    List<Student> collect = studentList.stream().
        filter(s -> s.getHeight() > 180)
        .sorted((s1, s2) -> s1.getHeight() - s2.getHeight())
        .collect(Collectors.toList());

    System.out.println(collect);
    // 对高于180的同学根据身高进行排序(从高到低)
    List<Student> studentList = new StudentList().studentList;
    List<Student> collect = studentList.stream().
        filter(s -> s.getHeight() > 180)
        .sorted((s1, s2) -> s2.getHeight() - s1.getHeight())
        .collect(Collectors.toList());

    System.out.println(collect);

5 终止操作

5.1 anyMatch(是否有任意一个匹配)

anyMatch:检查流到最后的数据, 是否有一个/多个数据匹配某种情况。

比如,流里面最终有10个元素, 使用anyMatch。 只要有一个为真,则为真。

//  boolean anyMatch(Predicate<? super T> predicate);
//  anyMatch: 判断该stream中的所有元素, 是否存在某个/某些元素,可以根据某个条件处理之后, 满足true

    //  判断是否存在北京的同学
    List<Student> studentList = new StudentList().studentList;
    boolean b1 = studentList.stream()
        .anyMatch(a -> {
          return a.getAddress() == Student.Address.BJ;
        });
    System.out.println(b1);

    //  判断高于190的是否存在北京的同学
		boolean b2 = studentList.stream()
        .filter(s -> s.getHeight() > 190)
        .anyMatch(a -> {
          return a.getAddress() == Student.Address.BJ;
        });
    System.out.println(b2);

5.2 allMatch(是否所有的全匹配)

allMatch:检查是否所有元素都匹配

// boolean allMatch(Predicate<? super T> predicate);
// allMatch: 判断该stream中的所有元素, 是否所有元素 可以根据某个条件处理之后, 满足true
    //  判断是否都是北京的同学
    List<Student> studentList = new StudentList().studentList;
    boolean b1 = studentList.stream()
        .allMatch(a -> {
          return a.getAddress() == Student.Address.BJ;
        });
    System.out.println(b1);
    //  判断高于200的是否都是上海的同学
    boolean b2 = studentList.stream().filter(s -> s.getHeight() >= 200)
        .allMatch(s -> s.getAddress() == Student.Address.SH);
    System.out.println(b2);

5.3 noneMatch(没有匹配)

noneMatch: 检查是否没有匹配元素

// boolean noneMatch(Predicate<? super T> predicate);
// noneMatch: 判断该stream中的所有元素, 是否所有元素 可以根据某个条件处理之后, 满足false

    List<Student> studentList = new StudentList().studentList;

    // 判断是否不存在深圳的同学
    boolean b1 = studentList.stream().noneMatch(s -> Student.Address.SZ.equals(s.getAddress()));

    System.out.println(b1);

5.4 findAny(找到任意一个)

findAny:返回流中任意元素: 默认第一个

// Optional<T> findAny();
// findAny: 返回任意元素(默认第一个)

        //  返回任意一个同学
    List<Student> studentList = new StudentList().studentList;

    Optional<Student> any = studentList.stream()
        .findAny();

    //TODO: 注意, Optional作为一个容器代表一个值存在或者不存在
    //TODO: Optional中存在几个方法, 可以让使用者显式的检查值存在或者不存在
    // <1>: isPresent()方法:  如果 Optional包含值返回true, 否则返回false
    // <2>: ifPresent(代码块)方法: 会将Optional包含的值, 传给指定的代码块
    // <3>: get()方法: 如果Optional包含值, 返回包含的值, 否则抛出异常
    // <4>: orElse(默认值):  如果Optional包含值, 返回包含的值, 否则返回默认值
    any.isPresent();
    any.ifPresent(a -> System.out.println(a));
    any.get();

    any.orElse(new Student("默认值", 18, 140, Student.Address.SH));

    System.out.println(any);

// 一般的用法
if (any.isPresent()){
  Student student = any.get();
  System.out.println(student);
}

 		// 返回任意一个身高小于170同学
    List<Student> studentList = new StudentList().studentList;

    // 找到任意一个小于170的
    Optional<Student> any = studentList.stream().filter(s -> s.getHeight() < 170).findAny();

    if (any.isPresent()) {
      Student student = any.get();
      System.out.println("这里有一个170以下的学生" + student);
    } else {
      System.out.println("没有170以下的学生");
    }

5.5 findFirst(找到第一个)

findFirst:返回第一个元素

//  Optional<T> findFirst();
//  findFirst: 返回第一个元素

    //  获得年龄最小的同学
    Optional<Student> first = studentList.parallelStream()
        .sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst();
    System.out.println(first.get());

5.6 forEach

forEach: 遍历流

//  void forEach(Consumer<? super T> action);
//  forEach: 遍历元素(void方法)

    //  遍历列表
    List<Student> studentList = new StudentList().studentList;
    studentList.stream()
        .sorted((o1, o2) -> o1.getAge() - o2.getAge())
        .forEach(a -> System.out.println(a));

5.7 count

count: 返回元素中数量

//  long count();
//  count: 计算元素个数

    //  北京同学的数量
    List<Student> studentList = new StudentList().studentList;

    long count = studentList.stream()
        .filter(a -> a.getAddress() == Student.Address.BJ)
        .count();
    System.out.println(count);

5.8 reduce

reduce: 计算元素

reduce: 将参加计算的元素按照某种方式减少。

  • 比如,两个元素比较,返回大的; 按照这个方式,最终会拿到最大的

  • 两个元素,返回和; 按照这个方式,最终会拿到所有的和

MapReduce。

 List<Person> personList = StudentList.personList;
// TODO: 规约 reduce
//  <1>一参情况: Optional<T> reduce(BinaryOperator<T> accumulator)
//  <2>二参情况: T reduce(T identity, BinaryOperator<T> accumulator);

//  1参数:
//  返回值类型为Optional, 是应对如果流中没有任何元素情况(这种情况没有初始值就无法返回结果)
//  所以1参是把结果包裹在一个Optional对象里(可以通过get方法获取),用以表明/处理结果可能不存在情况

//  2参数:
//  BinaryOperator: 将两个元素合起来产生一个新值
//  identity: 计算的初始值/起始值(用来和第一个元素计算结果)

    //  TODO:班级同学年龄总和
    Optional<Integer> optional = studentList.stream()
        .map(a -> a.getAge())
        .reduce((a, b) -> a + b);

    Integer sum = optional.get();
    System.out.println(sum);

List<Student> studentList = new StudentList().studentList;

    // 年龄最大的学生
    Optional<Integer> reduce2 = studentList.stream().map(a -> a.getAge()).reduce((a, b) -> {
      if (a > b) {
        return a;
      }
      return b;
    });

    Optional<Integer> reduce3 = studentList.stream().map(a -> a.getAge()).reduce(Integer::max);

    // 年龄最小的学生
    Optional<Integer> reduce4 = studentList.stream().map(a -> a.getAge()).reduce((a, b) -> {
      if (a > b) {
        return b;
      } else {
        return a;
      }
    });
    Optional<Integer> reduce5 = studentList.stream().map(a -> a.getAge()).reduce(Integer::min);


    System.out.println(reduce2 + "==" + reduce3);
    System.out.println(reduce4 + "==" + reduce5);

5.9 collect

collect: 收集器, 用于收集数据经过流计算的结果

5.9.1 收集

作用是将元素分别归纳进可变容器 ListMapSetCollection 或者ConcurrentMap

Collectors.toList()
Collectors.toCollection()
Collectors.toSet()
Collectors.toMap()
    List<Student> studentList = new StudentList().studentList;
    // 获取武汉同学的集合 toList
    List<Student> collect = studentList.stream().filter(a -> a.getAddress() == Student.Address.WH)
        .collect(Collectors.toList());

    System.out.println(collect);
// 获得武汉同学集合: toCollection
    LinkedList<Student> collect = studentList.stream().filter(a -> a.getAddress() == Student.Address.WH)
        .collect(Collectors.toCollection(() -> new LinkedList<>()));
 // 获得武汉同学集合: toSet
    Set<Student> studentSet = studentList.stream().filter(a -> a.getAddress() == Student.Address.WH)
        .collect(Collectors.toSet());
// 获得武汉同学集合(姓名和年龄): toMap
    Map<String, Integer> collect1 = studentList.stream().filter(a -> a.getAddress() == Student.Address.WH)
        .collect(Collectors.toMap(Student::getName, student -> student.getAge()));

    System.out.println(collect1);
// 获得武汉同学集合(姓名和对象本身)
    Map<String, Student> collect2 =
        studentList.stream().filter(a -> a.getAddress() == Student.Address.WH)
        .collect(Collectors.toMap(Student::getName, student -> student));

    System.out.println(collect2);
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaStreamJava8引入的一个新特性,用于处理集合和数组等数据源的元素。它是一种函数式编程风格的API,可以通过链式调用一系列的操作来实现对数据源的处理,包括过滤、映射、排序、聚合等操作。 Stream分为间操作和终止操作两种类型。间操作是指对数据源进行的转换操作,每次间操作都会返回一个新的Stream对象,可以链式调用多个间操作。终止操作是指对数据源进行的最终操作,会返回一个非Stream类型的结果,例如forEach、count、collect等。 以下是一个简单的示例,演示如何使用Stream对一个整数列表进行过滤、映射和统计操作: ```java List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 过滤出偶数 List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); // 将偶数加倍 List<Integer> doubledNumbers = evenNumbers.stream() .map(n -> n * 2) .collect(Collectors.toList()); // 统计偶数的个数 long count = evenNumbers.stream().count(); ``` 在上面的示例,我们首先创建了一个整数列表numbers,然后使用stream()方法将其转换为一个Stream对象。接着使用filter()方法过滤出偶数,并使用collect()方法将结果转换为一个List对象。然后使用map()方法将偶数加倍,并再次使用collect()方法将结果转换为一个List对象。最后使用count()方法统计偶数的个数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值