【Stream API笔记01】JDK1.8新特性之Stream API中常见的方法及其使用方式

这篇文章,主要介绍JDK1.8中新特性之Stream API中常见的方法及其使用方式。

目录

一、Stream API使用

1.1、Stream介绍

1.2、Stream创建对象

(1)empty()方法

(2)of()方法

(3)Arrays.stream()方法

(4)list.stream()方法

1.3、Stream中间方法

(1)filter()方法

(2)map()方法

(3)flatMap()方法

(4)limit()方法

(5)skip()方法

(6)sorted()方法

(7)distinct()方法

(8)peek()方法

1.4、Stream终端方法

(1)min()方法

(2)max()方法

(3)count()方法

(4)reduce()方法

(5)anyMatch()方法

(6)allMatch()方法

(7)noneMatch()方法

(8)findFirst()方法

(9)findAny()方法

(10)forEach()方法

(11)collect()方法


一、Stream API使用

1.1、Stream介绍

Stream是JDK1.8新增的一个特性,它是一个位于【java.util.stream】包下面的接口,接口中定义了用于操作数据的公共方法,Stream被称作:【流】,它的意图就是可以按照流的方式对数据进行一些加工处理,实际开发里面最常见的就是操作集合数据,通过Stream API可以编写很少的代码就完成某一个功能,相比于传统的集合处理方式,Stream API显示更加的优雅、高效。

Java中可以将Stream API方法大致分为两类,分别是:【中间方法】和【终端方法】。

  • 中间方法:在Stream流被消费之前,对Stream流中的数据进行一些加工处理,然后返回加工处理之后的新的Stream流。
  • 终端方法:Stream流经过中间方法的加工处理之后,最终是需要被消费的,调用终端方法之后,这个Stream流就结束了,之后就不能够在使用这个Stream流对象。

Stream流处理大致流程图:

下面详细介绍一下Stream API的常见方法。

1.2、Stream创建对象

Java中主要有下面四种方法创建一个Stream对象,分别如下所示:

  • 第一种:使用Stream提供的empty()方法。
  • 第二种:使用Stream提供的of()方法。
  • 第三种:使用Arrays.stream()数组工具类提供的方法。
  • 第四种:使用集合对象的stream()方法(这种方式在实际开发里面最常用)。

(1)empty()方法

Stream接口中提供了一个empty()方法,该方法用于创建一个空的Stream对象,基本上不会用这个方法吧,反正我是没用过^-^。

public class StreamDemo {
    public static void main(String[] args) {
        // 创建空的 Stream 对象
        Stream<Object> empty = Stream.empty();
    }
}

(2)of()方法

Stream接口中提供了两个of()方法,of()方法可以将传入的数据转换为一个Stream对象返回。

public class StreamDemo {
    public static void main(String[] args) {
        // 创建 Stream 对象
        Stream<String> stream = Stream.of("123");
        // 可变参数
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4);
    }
}

(3)Arrays.stream()方法

Arrays数组工具类中提供了一个stream()方法,该方法可以将数组转换为Stream流对象。

public class StreamDemo {
    public static void main(String[] args) {
        // 创建 Stream 对象
        int[] arr = new int[] {1, 2, 3, 4, 5};
        IntStream stream = Arrays.stream(arr);
    }
}

(4)list.stream()方法

Java集合类中提供了一个stream()方法,作用也是将Collection集合数据转换为Stream流对象。

public class StreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("111");
        list.add("222");
        list.add("333");
        // 创建 Stream 对象
        Stream<String> stream = list.stream();
    }
}

注意:只要是Collection集合类,它都具有stream()方法。并且这个也是最常用的方法,因为实际开发里面,集合是最常见的操作对象。

1.3、Stream中间方法

Stream中间方法是对Stream流中数据进行一些加工处理的,中间方法的返回值都是一个新的Stream流对象,这个Stream流对象就是加工处理之后的新对象。

(1)filter()方法

filter()方法用于过滤Stream流中的数据,该方法需要传递一个【Predicate】对象(一个函数式接口),可以采用lambada表达式编写过滤条件。

public class StreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("111");
        list.add("222");
        list.add("333");
        // 创建 Stream 对象
        Stream<String> stream = list.stream();
        // 过滤方法: 过滤出集合中不等于 222 的数据
        Stream<String> stream1 = stream.filter(item -> !item.equals("222"));
    }
}

(2)map()方法

map()方法主要作用就是将一个Stream流映射成一个新的Stream流,map方法使用最为频繁,比如:从集合中提取某个对象的某个属性,将其组成新的一个集合,下面看下两种实现方式。

  • 没有使用Stream.map()方法的代码。
public static void main(String[] args) {
    List<User> userList = getUserList();
    // 从集合中提取 姓名
    List<String> nameList = new ArrayList<>();
    for (int i = 0; i < userList.size(); i++) {
        nameList.add(userList.get(i).getName());
    }
}
  • 采用Stream.map()方法的代码。
public static void main(String[] args) {
    List<User> userList = getUserList();
    // 从集合中提取 姓名
    List<String> nameList = userList.stream().map(item -> item.getName()).collect(Collectors.toList());
}

对比上面两种方式,可以看到,采用Stream API编写的代码是多么的优雅,并且代码也简化了很多。

(3)flatMap()方法

flatMap()方法和map()方法的作用是类似的,都是用于映射Stream流的,只不过flatMap()方法可以将多个不同的Stream流映射为一个Stream流对象,flat表示【扁平化】的意思,就好比将多行数据合并成一行数据,这个就是扁平化。

public class StreamDemo {
    public static List<List<User>> getUserList() {
        List<List<User>> ans = new ArrayList<>();
        List<User> list = new ArrayList<>();
        list.add(new User(333, "name_003", "pass_003"));
        list.add(new User(111, "name_001", "pass_001"));
        list.add(new User(444, "name_004", "pass_004"));
        ans.add(list);
        List<User> list2 = new ArrayList<>();
        list2.add(new User(222, "name_002", "pass_002"));
        ans.add(list2);
        return ans;
    }
    public static void main(String[] args) {
        List<List<User>> userList = getUserList();
        System.out.println(userList);
        // 将集合中的集合,转换为一个集合
        List<User> ansList = userList.stream().flatMap(item -> item.stream()).collect(Collectors.toList());
        System.out.println(ansList);
    }
}

上面案例中,由于集合里面的元素又是集合,为了将整个集合变成一个集合,通过Stream中的flatMap()方法,将多个Stream流映射成一个Stream对象。

(4)limit()方法

limit()方法作用是限定数据的条数,比如:集合中有100条数据,通过limit()方法可以只获取10条数据。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("111");
    list.add("222");
    list.add("333");
    list.add("444");
    list.add("555");
    // 限定5条数据
    Stream<String> stream = list.stream().limit(5);
}

(5)skip()方法

skip()方法作用是:跳过多少条数据,然后在开始处理数据。比如:现在有100条数据,前10条数据是不需要的,从第11条数据开始进行处理,这个时候就可以通过skip()方法跳过前10条数据进行处理。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("111");
    list.add("222");
    list.add("333");
    list.add("444");
    list.add("555");
    // 跳过前 2 条数据
    Stream<String> skip = list.stream().skip(2);
    // 打印结果
    skip.forEach(System.out::println);
}

打印结果如下所示:

(6)sorted()方法

sorted()方法作用是将数据进行排序,sorted()有两个方法,一个是无参的sorted()方法,另一个是有参数的sorted(Comparator)方法。

  • sorted()无参方法,默认按照自然排序规则,升序排序所有数据,即:数值类按照从小到大排序,字符串按照字典序排序。
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("333");
    list.add("111");
    list.add("444");
    list.add("222");
    list.add("555");

    System.out.println("排序前: ");
    list.forEach(item -> System.out.print(item + " "));
    System.out.println();

    // 排序
    Stream<String> sorted = list.stream().sorted();
    System.out.println("排序后: ");
    sorted.forEach(item -> System.out.print(item + " "));
}

运行结果如下所示:

  • sorted()有参方法,自定义排序比较器,自定义排序规则。
public class StreamDemo {
    public static List<User> getUserList() {
        List<User> list = new ArrayList<>();
        list.add(new User(333, "name_003", "pass_003"));
        list.add(new User(111, "name_001", "pass_001"));
        list.add(new User(444, "name_004", "pass_004"));
        list.add(new User(222, "name_002", "pass_002"));
        return list;
    }
    public static void main(String[] args) {
        List<User> userList = getUserList();
        System.out.println("排序前: ");
        userList.forEach(System.out::println);

        // 自定义排序
        Stream<User> sorted = userList.stream().sorted(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getId() - o2.getId();
            }
        });

        System.out.println("排序后: ");
        sorted.forEach(System.out::println);
    }
}

运行结果如下所示:

(7)distinct()方法

distinct()方法作用是将数据中的重复数据去重操作,只保留一条数据。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("111");
    list.add("222");
    list.add("333");
    list.add("444");
	// 重复数据
    list.add("555");
    list.add("555");
    // 去重
    Stream<String> distinct = list.stream().distinct();
    // 打印结果
    distinct.forEach(System.out::println);
}

打印结果如下所示:

(8)peek()方法

peek()方法接收一个Consumer消费者对象,消费者可以对流中的数据进行修改操作或者访问操作。

public class StreamDemo {
    public static List<User> getUserList() {
        List<User> list = new ArrayList<>();
        list.add(new User(333, "name_003", "pass_003"));
        list.add(new User(111, "name_001", "pass_001"));
        list.add(new User(444, "name_004", "pass_004"));
        list.add(new User(222, "name_002", "pass_002"));
        return list;
    }
    public static void main(String[] args) {
        List<User> userList = getUserList();
        // peek 修改数据
        Stream<User> peek = userList.stream().peek(item -> item.setName("demo_peek"));
        peek.forEach(System.out::println);
    }
}

注意:Stream中间方法的执行操作并不会立即生效,只有调用终端方法之后,中间方法的执行效果才会生效。

1.4、Stream终端方法

终端方法是将Stream流消费之后,并且最终会得到一个结果,常见的终端方法下面这些。

(1)min()方法

min()方法主要作用:从数据流中计算出最小值元素。min()方法需要提供一个Comparator比较器对象,通过比较器器对象的比较规则,找出最小值元素。

注意:min()方法不是说找出最小的元素,它是根据你指定的比较器规则找出最小元素。

比如下面这个例子,集合元素是【1,2,3,4,5】,但是比较器规则是后一个元素减去前一个元素。

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    Optional<Integer> min = list.stream().min(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    Integer ans = min.get();
    System.out.println(ans);
}

上面输出结果是:【5】。

(2)max()方法

max()方法作用:从数据流中计算出最小值元素。参数和min()方法是相同的。

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    Optional<Integer> max = list.stream().max(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    Integer ans = max.get();
    System.out.println(ans);
}

上面输出结果是:【1】。

(3)count()方法

count()方法作用:统计Stream流中数据元素个数,返回值是long类型。

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(2, 1, 5, 4, 3);
    // 计算个数
    long count = list.stream().count();
    System.out.println(count);
}

(4)reduce()方法

reduce()方法作用:将stream流中数据进行规约操作,最终得到一个结果。这里的规约就类似于迭代,比如:要计算stream流中所有数据的和,那就可以使用reduce()方法进行规约计算。

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(2, 1, 5, 4, 3);
    // 规约操作
    Optional<Integer> ans = list.stream().reduce((a, b) -> a + b);
    System.out.println(ans.get());
    // 指定初始值:
    // 这里指定第一个参数值是10,也就是reduce的规约操作的初始值是10
    int ans1 = list.stream().reduce(10, (a, b) -> a + b);
    System.out.println(ans1);

    // 这里结果等于:1200
    int ans2 = list.stream().reduce(10, (a, b) -> a * b);
    System.out.println(ans2);
}

运行结果如下所示:

reduce()规约操作分析:

(5)anyMatch()方法

anyMatch()方法作用:只要stream数据流中【存在一个满足匹配】条件的数据,那么方法就会返回true,否则返回false。

(6)allMatch()方法

allMatch()方法作用:只有当stream数据流中的所有数据,【都满足匹配】条件的数据,那么方法才会返回true,否则返回false。

(7)noneMatch()方法

allMatch()方法作用:只有当stream数据流中的所有数据,【都不满足匹配】条件的数据,那么方法才会返回true,否则返回false。

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(2, 1, 5, 4, 3);
    // 满足一个: 集合中有一个元素是大于4的
    boolean match = list.stream().anyMatch(item -> item > 4);
    System.out.println(match);
    // 都满足: 集合中所有元素都小于6
    boolean match1 = list.stream().allMatch(item -> item < 6);
    System.out.println(match1);
    // 都不满足: 集合中不存在大于 6 的元素
    boolean match2 = list.stream().noneMatch(item -> item > 6);
    System.out.println(match2);
}

(8)findFirst()方法

findFirst()方法作用:从stream流中获取第一条数据。

public class StreamDemo {
    public static List<User> getUserList() {
        List<User> list = new ArrayList<>();
        list.add(new User(333, "name_003", "pass_003"));
        list.add(new User(111, "name_001", "pass_001"));
        list.add(new User(444, "name_004", "pass_004"));
        list.add(new User(222, "name_002", "pass_002"));
        return list;
    }
    public static void main(String[] args) {
        List<User> userList = getUserList();
        // 获取第一条数据
        Optional<User> first = userList.stream().findFirst();
        if (first.isPresent()) {
            System.out.println(first.get());
        }
    }
}

(9)findAny()方法

findAny()方法作用:从stream数据流中随机返回一条数据。

(10)forEach()方法

forEach()方法作用:遍历stream流中的数据元素。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("333");
    list.add("111");
    list.add("444");
    list.add("222");
    list.add("555");
    list.stream().forEach(item -> {
        System.out.println(item);
    });
}

(11)collect()方法

collect()方法作用:用于将中间方法处理的stream流收集为一个结果,常见的如:List、Set、Map等等。

public class StreamDemo {
    public static List<User> getUserList() {
        List<User> list = new ArrayList<>();
        list.add(new User(333, "name_003", "pass_003"));
        list.add(new User(111, "name_001", "pass_001"));
        list.add(new User(444, "name_004", "pass_004"));
        list.add(new User(222, "name_002", "pass_002"));
        return list;
    }
    public static void main(String[] args) {
        List<User> userList = getUserList();
        List<String> ansList = userList.stream().map(User::getName).collect(Collectors.toList());
        System.out.println(ansList);
    }
}

这个collect()方法更多的用法下一篇文章详细介绍。

到这里,Stream API常见方法就介绍完了,灵活的使用这些方法可以在很大程度上提高开发效率,使用之前需要确保你的项目是基于JDK1.8及以上版本运行的。

综上,这篇文章介绍,主要介绍JDK1.8中新特性之Stream API中常见的方法及其使用方式。


版权声明:本文为qq_39826207原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。