Java 8 Stream API

来源:互联网 发布:淘宝买高达模型 编辑:程序博客网 时间:2024/06/06 19:33

1.创建Stream

流(Stream) 到底是什么呢?是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合讲的是数据,流讲的是计算。Stream遵循“做什么,而不是怎么去做”的原则。

Stream与集合的区别:

  • Stream 自己不会存储元素。元素可能被存储在底层的集合中,或者根据需要产生出来。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream操作的三个步骤:

  • 创建一个Stream。
    一个数据源(如:集合、数组),获取一个流。
  • 中间操作。
    一个中间操作链,对数据源的数据进行处理。
  • 终止操作。
    一个终止操作,执行中间操作链,并产生结果。

Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:

  • default Stream stream():返回一个顺序流。
  • default Stream parallelStream():返回一个并行流。

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

  • static Stream stream(T[] array):返回一个流。
  • static IntStream stream(int[] array)
  • static LongStream stream(long[] array)
  • static DoubleStream stream(double[] array)

可以使用静态方法 Stream.of(),通过显示值创建一个流。它可以接收任意数量的参数。

  • static Stream of(T… values) : 返回一个流。

可以使用静态方法 Stream.iterate()和Stream.generate(),创建无限流。

  • static Stream iterate(final T seed, final UnaryOperator f)
  • static Stream generate(Supplier s)
    @Test    public void test1() {        // 1. Collection 提供了两个方法 stream() 与 parallelStream()        List<String> list = new ArrayList<>();        // 获取一个顺序流        Stream<String> stream = list.stream();        // 获取一个并行流        Stream<String> parallelStream = list.parallelStream();        // 2. 通过 Arrays 中的 stream() 获取一个数组流        Integer[] nums = new Integer[10];        Stream<Integer> stream1 = Arrays.stream(nums);        // 3. 通过 Stream 类中静态方法 of()        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);        // 4. 创建无限流        // 迭代        Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);        stream3.forEach(System.out::println);        // 生成        Stream<Double> stream4 = Stream.generate(Math::random).limit(2);        stream4.forEach(System.out::println);    }

2.Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。而在终止操作时一次性全部处理,称为“惰性求值”。

1.筛选与切片

filter(Predicate p):接收 Lambda , 从流中排除某些元素。
distinct():筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。
limit(long maxSize):截断流,使其元素不超过给定数量。
skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补。

2.映射

map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

3.排序

sorted() :产生一个新流,其中按自然顺序排序。
sorted(Comparator comp):产生一个新流,其中按比较器顺序排序。

3.Stream的终止操作

终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

1.查找与匹配

allMatch(Predicate p):检查是否匹配所有元素。
anyMatch(Predicate p):检查是否至少匹配一个元素。
noneMatch(Predicate p):检查是否没有匹配所有元素。
findFirst():返回第一个元素。
findAny():返回当前流中的任意元素。
count():返回流中元素总数。
max(Comparator c):返回流中最大值。
min(Comparator c):返回流中最小值。
forEach(Consumer c):内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)。

2.归约(也叫聚合操作)

reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional。

3.收集结果

collect(Collector c):将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法。

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下。

  • toList:把流中元素收集到List。

    List< Employee > emps = list.stream().collect(Collectors.toList());

  • toSet:把流中元素收集到Set。

    Set< Employee > emps = list.stream().collect(Collectors.toSet());

  • toCollection:把流中元素收集到创建的集合。

    ArrayList< Employee > emps = list.stream().collect(Collectors.toCollection(ArrayList::new));

  • counting:计算流中元素的个数。

    long count = list.stream().collect(Collectors.counting());

  • summingInt:对流中元素的整数属性求和。

    int total = list.stream().collect(Collectors.summingInt(Employee::getSalary));

  • averagingInt:计算流中元素Integer属性的平均值。

    double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));

  • summarizingInt:收集流中Integer属性的统计值。如:平均值。

    IntSummaryStatistics iss = list.stream().collect(Collectors.summarizingInt(Employee::getSalary));

  • joining:连接流中每个字符串。

    String str = list.stream().map(Employee::getName).collect(Collectors.joining());

  • maxBy:根据比较器选择最大值。

    Optional< Emp > max = list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));

  • minBy:根据比较器选择最小值。

    Optional< Emp > min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));

  • reducing:从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值。

    int total = list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));

  • collectingAndThen:包裹另一个收集器,对其结果转换函数。
    inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));

  • groupingBy:根据某属性值对流分组,属性为K,结果为V。

    Map< Emp.Status, List< Emp>> map = list.stream().collect(Collectors.groupingBy(Employee::getStatus));

  • partitioningBy:根据true或false进行分区。

    Map< Boolean, List< Emp >> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));

// Testpublic class Trader {    private String name;    private String city;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getCity() {        return city;    }    public void setCity(String city) {        this.city = city;    }    public Trader(String name, String city) {        super();        this.name = name;        this.city = city;    }    public Trader() {        super();    }    @Override    public String toString() {        return "Trader [name=" + name + ", city=" + city + "]";    }}public class Transaction {    private Trader trader;    private int year;    private int value;    public Transaction() {    }    public Transaction(Trader trader, int year, int value) {        this.trader = trader;        this.year = year;        this.value = value;    }    public Trader getTrader() {        return trader;    }    public void setTrader(Trader trader) {        this.trader = trader;    }    public int getYear() {        return year;    }    public void setYear(int year) {        this.year = year;    }    public int getValue() {        return value;    }    public void setValue(int value) {        this.value = value;    }    @Override    public String toString() {        return "Transaction [trader=" + trader + ", year=" + year + ", value=" + value + "]";    }}public class TestTransaction {    List<Transaction> transactions = null;    @Before    public void before() {        Trader raoul = new Trader("Raoul", "Cambridge");        Trader mario = new Trader("Mario", "Milan");        Trader alan = new Trader("Alan", "Cambridge");        Trader brian = new Trader("Brian", "Cambridge");        transactions = Arrays.asList(                new Transaction(brian, 2011, 300),                new Transaction(raoul, 2012, 1000),                new Transaction(raoul, 2011, 400),                new Transaction(mario, 2012, 710),                new Transaction(mario, 2012, 700),                new Transaction(alan, 2012, 950)        );    }    /**     * 1. 找出2011年发生的所有交易, 并按交易额排序(从低到高)     */    @Test    public void test1() {        transactions.stream()                    .filter((t) -> t.getYear() == 2011)                    .sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))                    .forEach(System.out::println);    }    /**     * 2. 交易员都在哪些不同的城市工作过?     */    @Test    public void test2() {        transactions.stream()                    .map((t) -> t.getTrader().getCity())                    .distinct()                    .forEach(System.out::println);    }    /**     * 3. 查找所有来自剑桥的交易员,并按姓名排序     */    @Test    public void test3() {        transactions.stream()                    .filter((t) -> t.getTrader().getCity().equals("Cambridge"))                    .sorted((t1, t2) -> t1.getTrader().getName().compareTo(t2.getTrader().getName()))                    .forEach(System.out::println);    }    /**     * 4. 返回所有交易员的姓名字符串,按字母顺序排序     */    @Test    public void test4() {        transactions.stream()                    .map((t) -> t.getTrader().getName())                    .sorted()                    .forEach(System.out::println);        System.out.println("--------------------------");        String reduce = transactions.stream()                    .map((t) -> t.getTrader().getName())                    .sorted()                    .reduce("", String::concat);        System.out.println(reduce);        System.out.println("--------------------------");        transactions.stream()                    .map((t) -> t.getTrader().getName())                    .flatMap(TestTransaction::filterCharacter)                    .sorted((t1, t2) -> t1.compareToIgnoreCase(t2))                    .forEach(System.out::println);    }    public static Stream<String> filterCharacter(String str){        List<String> list = new ArrayList<>();        for (Character ch : str.toCharArray()) {            list.add(ch.toString());        }        return list.stream();    }    /**     * 5. 有没有交易员是在米兰工作的?     */    @Test    public void test5() {        boolean b = transactions.stream()                    .anyMatch((t) -> t.getTrader().getCity().equals("Milan"));        System.out.println(b);    }    /**     * 6. 打印生活在剑桥的交易员的所有交易额     */    @Test    public void test6() {        Optional<Integer> optional = transactions.stream()                    .filter((t) -> t.getTrader().getCity().equals("Cambridge"))                    .map((t) -> t.getValue())                    .reduce(Integer::sum);        System.out.println(optional.get());    }    /**     * 7. 所有交易中,最高的交易额是多少     */    @Test    public void test7() {        Optional<Integer> max = transactions.stream()                    .map((t) -> t.getValue())                    .max(Integer::compareTo);        System.out.println(max.get());    }    /**     * 8. 找到交易额最小的交易     */    @Test    public void test8() {        Optional<Transaction> min = transactions.stream()                    .min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));        System.out.println(min.get());    }}

将聚合的结果收集到Map中,可以使用Collections.toMap()方法,有2个参数,分别用来生产map的key和value。(java.util.stream.Collectors)如果有多个元素拥有相同的key,则会抛出一个IllegalStateException异常。

    @Test    public void test1() {        Map<Integer, String> map = emps.stream()            .collect(Collectors.toMap(Employee::getId, Employee::getName));        for(Integer i : map.keySet()) {            System.out.println(map.get(i));        }    }    @Test    public void test2() {        Map<Integer, Employee> map = emps.stream()            .collect(Collectors.toMap(Employee::getId, Function.identity()));        for(Integer i : map.keySet()) {            System.out.println(map.get(i));        }    }

对于toMap方法的每种形式,都有一个对应的toConcurrentMap方法,用来产生一个并发的Stream。在并行收集的过程中,应当只使用一个并发的map。一个共享的map要比合并的map效率更高。当然,使用共享的map无法得到有序的结果。

4.原始信息流

对于Stream< Integer >、Stream< Long >、Stream< Double >这样的流,不过将每个数包装成相应的对象显然是一个低效的做法,Stream API提供了IntStream、LongStream、DoubleStream等类型,专门用来存储原始类型值。

        IntStream intStream = IntStream.of(1,2,3,4,5,6);        DoubleStream doubleStream = DoubleStream.of(1f,2f,3f,4f);        LongStream longStream = LongStream.of(1l,2l,3l,4l,5l,6l);        Arrays.stream(value, from, to);

IntStream和LongStream有静态方法range()和rangeClosed(),用来产生一个范围内的整数。range(0, 100)和rangeClosed(0, 100),前者不包括上限,后者包括上限。

将对象刘转化为原始类型流,通过mapToLong、mapToInt、mapToDouble方法。

Stream<String> words = ...;IntStream intStream = words.mapToInt(String::length);

将原始类型流转化为对象流,通过boxed方法。

Stream<Integer> stream = IntStream.range(0, 100).boxed();

注意:Random类现在提供了ints、longs、doubles这些方法,用来返回包含随机数的原始类型流。

5.Optional< T >类

Optional< T > 类(java.util.Optional) 是一个容器类,Optional< T >对象或者是对一个T类型对象的封装,或者表示不是任何对象。它比一般指向T类型的引用更安全,因为它不会返回null,可以避免空指针异常。

它的实现代码很少,只有350行,下面就对一些常用方法进行介绍:

// 只有2个成员变量
private static final Optional< ? > EMPTY = new Optional<>();
private final T value;

  • Optional.of(T t):创建一个 Optional 实例。
    public static <T> Optional<T> of(T value) {        return new Optional<>(value);    }
  • Optional.empty():创建一个空的 Optional 实例。
    public static<T> Optional<T> empty() {        @SuppressWarnings("unchecked")        Optional<T> t = (Optional<T>) EMPTY;        return t;    }
  • Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例。
    public static <T> Optional<T> ofNullable(T value) {        return value == null ? empty() : of(value);    }
  • get():如果对象存在,会返回该对象,否则会抛出一个NoSuchElementException异常。
    public T get() {        if (value == null) {            throw new NoSuchElementException("No value present");        }        return value;    }
  • isPresent():判断是否包含值。
    public boolean isPresent() {        return value != null;    }
  • orElse(T t):如果调用对象包含值,返回该值,否则返回t。
    public T orElse(T other) {        return value != null ? value : other;    }
  • orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值。
    public T orElseGet(Supplier<? extends T> other) {        return value != null ? value : other.get();    }`
  • map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()。
    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {        Objects.requireNonNull(mapper);        if (!isPresent())            return empty();        else {            return Optional.ofNullable(mapper.apply(value));        }    }
  • flatMap(Function mapper):与 map 类似,要求返回值必须是Optional。
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {        Objects.requireNonNull(mapper);        if (!isPresent())            return empty();        else {            return Objects.requireNonNull(mapper.apply(value));        }    }

使用Optional可以避免空指针异常,高效的使用Optional的关键在于,使用一个或者接受正确值、或者返回另一个替代值的方法。以前使用 !=null判断, 现在使用Optional.isPresent()判断。

    @Test    public void test2() {        Optional<Employee> optional = Optional.empty();        if (optional.isPresent()) {            System.out.println(optional.get());        } else {            System.out.println("optional is null");        }    }    // 如果optional中没有该值,就返回一个默认值。    @Test    public void test3() {        Optional<Employee> optional = Optional.empty();        Employee employee = optional.orElse(new Employee());        System.out.println(employee);    }

6.并行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。默认情况下,流操作会创建一个串行流。Collection.parallelStream()可以创建一个并行流,parallel()方法可以将任意的串行流转换为一个并行流。

Stream< String > parallelWords = Stream.of(words).parallel();

记住,流不会收集它自己的数据,这些数据总是存在与另一个集合之中。如果你想要修改原有的集合,吗、、那么就无法定义流操作的输出。

        // ok        List<String> list = new ArrayList<>();        Stream<String> stream = list.stream();        list.add("hello");        long count = stream.distinct().count();        // error        List<String> list2 = new ArrayList<>();        Stream<String> stream2 = list2.stream();        stream2.forEach(s -> {            if (s.length() < 12) {                list2.remove(s);            }        });

关于并行的操作,还在学习中,不是很了解…

0 0
原创粉丝点击