Java8新特性之流式编程Stream API

1. 流式思想概述

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序把原材料加工成一个商品。
在这里插入图片描述
Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,提供了一种高效且易于使用的处理数据的方式。
在这里插入图片描述
这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。而最右侧的数字3是最终结果。

Stream API是用来处理集合、数组等容器中的数据的,处理操作有:查询、筛选、删除、过滤、统计、映射等。集合、数组讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据。

Stream的执行流程:

1、获取Stream流:通过一个数据源(如:集合、数组),获取一个流

2、中间处理操作:中间操作是个操作链,对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行

3、终结操作:一旦执行终结操作,就执行中间操作链,最终产生结果并结束Stream。终结操作执行后就不能再对数据进行加工处理,如果要加工需要重新创建Stream。

Stream流的特点:

(1)Stream流本身不负责存储数据,存储数据是用集合、数组等数据结构。

(2)Stream流不会修改数据源的数据,每次处理都会返回一个持有结果的新Stream流对象。

(3)Stream流的操作是一个延迟操作,不调用终结方法 中间处理操作不会执行

注意:Stream流和IO(InputStream/OutputStream)流没有任何关系。

2. 获取stream流的方式

java.util.stream.Stream<T>是Java 8新增的stream流接口。(这并不是一个函数式接口。)

获取一个流非常简单,有以下几种常用的方式:

  • Collection接口中的默认方法default Stream<E> stream(),所有的Collection集合都可以通过该方法获取流对象
  • 通过数组工具类Arrays类的静态方法public static <T> Stream<T> stream(T[] array)获取数组对应的流对象。
  • Stream接口的静态方法public static<T> Stream<T> of(T... values)获取数组对应的流对象

根据Collection集合获取流

Collection接口的所有实现类均可通过stream方法获取流。

@Test
    public void test1() {
        List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
        Stream<Integer> stream = list.stream();
    }

根据数组获取流

@Test
public void test2() {
    String[] array = {"aaa", "bbb", "ccc", "ddd"};
    Stream<String> stream1 = Arrays.stream(array);
    Integer[] array2 = {1, 4, 3, 10, 12, 21};
    Stream<Integer> stream2 = Arrays.stream(array2);
}

@Test
public void test3() {
    String[] array = {"aaa", "bbb", "ccc", "ddd"};
    Stream<String> stream1 = Stream.of(array);

    /*int[] array2 = {1, 4, 3, 10, 12, 21};
        Stream<int[]> stream2 = Stream.of(array2);*/
    Integer[] array2 = {1, 4, 3, 10, 12, 21};
    Stream<Integer> stream2 = Stream.of(array2);
}

3. 常用方法:

Stream API 分为两类:终结方法和非终结方法。

  • 非终结方法:返回值类型仍然是Stream接口类型的方法,因此支持链式调用
  • 终结方法:返回值类型不再是Stream接口类型的方法,因此不再支持链式调用

注意:

  • 非终结方法是延迟执行的,调用终结方法时,才会真正执行前面的非终结方法
  • Stream API 仅对Stream流中的数据进行加工处理,不会修改数据源的数据。

forEach : 逐一处理

终结方法,调用该方法后不能调用其他方法,方法声明:

void forEach(Consumer<? super T> action);

该方法接收一个Consumer接口对象的参数,会对每一个元素进行逐一处理,例如遍历:

@Test
public void test1() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    Stream<Integer> stream = list.stream();
    //可传入lambda表达式、方法引用:Consumer<Integer> consumer = System.out::println;
    stream.forEach(System.out::println);
}

count:统计流中的元素个数

终结方法,方法声明:

long count();

代码示例

@Test
public void test2() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    Stream<Integer> stream = list.stream();
    long count = stream.count();
    System.out.println(count); //6
}

filter:过滤

非终结方法,方法声明:

Stream<T> filter(Predicate<? super T> predicate);

该方法接收一个Predicate函数式接口对象的参数作为筛选条件

筛选出集合中的偶数,代码示例:

@Test
public void test3() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    //链式调用
    long count = list.stream().filter(i -> i % 2 == 0).count();
    System.out.println(count);
}

limit:取出前n个元素

非终结方法,方法声明:

Stream<T> limit(long maxSize);

参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作

代码示例:

@Test
public void test4() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    list.stream().limit(4).forEach(System.out::println);
}

skip:跳过前n个元素

非终结方法,方法声明:

Stream<T> skip(long n): 

跳过前n个元素,获取一个截取之后的新流,如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流。

代码示例:

@Test
public void test5() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    list.stream().skip(3).forEach(System.out::println); //10 12 21
}

sorted:排序

自然排序:

Stream<T> sorted();

代码示例:

@Test
public void test6() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    list.stream().sorted().forEach(System.out::println);
}

定制排序:

Stream<T> sorted(Comparator<? super T> comparator);
@Test
public void test6() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    list.stream().sorted((i1, i2) -> -Integer.compare(i1, i2)).forEach(System.out::println);
}

map:映射

非终结方法,方法声明:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

将流中的元素映射到另一个流中

代码示例:

@Test
public void test7() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    //Function<Integer, String> mapper = i -> i + "a";
    list.stream().map(i -> i + "a").forEach(System.out::println); //1a 4a 3a 10a 12a 21a
}

distinct:去除重复数据

非终结方法,方法声明:

Stream<T> distinct();

代码示例:

@Test
public void test8() {
    List<Integer> list = Arrays.asList(4, 1, 4, 3, 10, 3, 12, 21, 3);
    list.stream().distinct().forEach(System.out::println);
}

自定义类型是根据对象的hashCode和equals来去除重复元素的:

public class Student {
    private String name;
    private int age;

    //构造器、hashcode、equals等
}
@Test
public void test9() {
    Stream.of(new Student("jack", 12), new Student("lily", 17)
    , new Student("mark", 17), new Student("jack", 12)
    , new Student("lily", 15), new Student("john", 16))
            .distinct().forEach(System.out::println);
}

reduce:归约

将流中元素反复结合起来,得到一个值

终结方法,方法声明:

T reduce(T identity, BinaryOperator<T> accumulator);
@Test
public void test11() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    //reduce:
    //第一次,将初始值赋值给i1,取出第一个元素赋值给i2
    //第二次,将第一次的结果赋值给i1,取出二个元素赋值给i2
    //第三次,将第二次的结果赋值给i1,取出三个元素赋值给i2
    //第四次,将第三次的结果赋值给i1,取出四个元素赋值给i2
    System.out.println(list.stream().reduce(5, (i1, i2) -> i1 + i2)); //56
}
Optional<T> reduce(BinaryOperator<T> accumulator);
@Test
public void test12() {
    List<Integer> list = Arrays.asList(1, 4, 3, 10, 12, 21);
    Optional<Integer> sum = list.stream().reduce((i1, i2) -> i1 + i2);
    System.out.println(sum.get()); //51
}

concat:组合

非终结方法,将两个Stream流对象合并成一个新的Stream流对象,该方法为静态方法,方法声明:

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

代码示例:

@Test
public void test13() {
    Stream<String> streamA = Stream.of("ccc", "abc", "bbb");
    Stream<String> streamB = Stream.of("aaa", "eee", "ddd");
    Stream.concat(streamA, streamB).forEach(System.out::println);
}

collect:收集Stream流中的结果到指定结构

Stream流中的结果收集到集合中

java.util.stream.Collector<T, A, R> 接口对象来指定收集到哪种集合中,java.util.stream.Collectors 类提供一些方法来返回 Collector 接口的对象。

@Test
public void test14() {
    //转为list集合
    List<String> list = Stream.of("ccc", "abc", "bbb")
        .collect(Collectors.toList());
    System.out.println(list);
    ArrayList<String> arrayList = Stream.of("ccc", "abc", "bbb")
        .collect(Collectors.toCollection(ArrayList::new));
    System.out.println(arrayList);

    //转为set集合
    Set<String> set = Stream.of("aaa", "eee", "ddd")
        .collect(Collectors.toSet());
    System.out.println(set);
    HashSet<String> hashSet = Stream.of("aaa", "eee", "ddd")
        .collect(Collectors.toCollection(HashSet::new));
    System.out.println(hashSet);
}

Stream流中的结果收集到数组中

Stream流提供toArray方法将结果收集到一个数组中,返回值类型为Object[],方法声明:

Object[] toArray();
@Test
public void test15() {
    Object[] array = Stream.of("ccc", "abc", "bbb")
            .toArray();
    System.out.println(Arrays.toString(array)); //[ccc, abc, bbb]
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值