stream

JAVA8新特性之Stream

一、Stream

  • Stream让开发者能够以一种声明的方式处理数据源(集合、数组等)

  • Stream专注于对数据源进行各种高效的聚合操作和大批量数据操作

  • Stream API 处理的数据源看着一种Stream(流),Stream在Pipeline(管道)中传输和运算(筛选、排序、聚合)后得到最终的处理结果

  • Stream API的使用离不开lambda表达式,可以提高编码效率和代码可读性

  • Stream API提供串行和并行两种操作,并行操作能发挥多核处理优势,使用fork/join的方式进行并行操作以提高运行速度。并行操作无需编写多线程代码即可写出高效的并发程序,且可避免多线程代码出错的问题

  • 在新版的JPA中,也已经加入了Stram

     @Query("select u from User u")
     Stream<User> findAllByCustomQueryAndStream();
    

<1>、了解几个核心概念

元素 Stream是一个来自数据源的元素队列,Stream本身并不存储元素。

数据源(即Stream的来源)包含集合、数组、I/O channel、generator(发生器)等。

聚合操作 类似SQL中的filter、map、find、match、sorted等操作

管道运算 Stream在Pipeline中运算后返回Stream对象本身,这样多个操作串联成一个Pipeline,并形成fluent风格的代码。这种方式可以优化操作,如延迟执行(laziness)和短路( short-circuiting)。

内部迭代 不同于java8以前对集合的遍历方式(外部迭代),Stream API采用访问者模式(Visitor)实现了内部迭代。

并行运算 Stream API支持串行(stream() )或并行(parallelStream() )的两种操作方式。


<2>、核心

  1. 创建Stream:从一个数据源(集合、数组)中获取流
  2. 中间操作:一个操作的中间链,对数据源的数据进行操作
  3. 终止操作:一个终止操作,执行中间操作链,并产生结果(操作完需要关闭)
  1. 创建Stream

    #集合创建流
    List list = new arraryList();
    list.add(a);
    list.add(b);
    list.add(c);
    Stream<list> stream = list.stream();
    #数组创建流
    String[] arrary = new String(){a,b,c}
    Stram<String> stream = Stream.of(arrary);
    
  2. 中间操作

    • filter:接收Lambda,从流中排除某些操作;
    • limit:截断流,使其元素不超过给定对象
    • skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
    • distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
    • map–接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    • flatMap–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
    • sorted()–自然排序(Comparable)
    • sorted(Comparator com)–定制排序(Comparator)
  3. 终止操作–查找与匹配

    • allMatch–检查是否匹配所有元素
    • anyMatch–检查是否至少匹配一个元素
    • noneMatch–检查是否没有匹配所有元素
    • findFirst–返回第一个元素
    • findAny–返回当前流中的任意元素
    • count–返回流中元素的总个数
    • max–返回流中最大值
    • min–返回流中最小值

<3>、串行流和并行流

stream()——串行流

parallelStream()——并行流,使用parallelStream()生成并行流后,对集合元素的遍历是无序的

  1. 看源码

        default Stream<E> stream() {
            return StreamSupport.stream(spliterator(), false);
        }
    
        default Stream<E> parallelStream() {
            return StreamSupport.stream(spliterator(), true);
        }
    
  2. 看列子

        public static void main(String[] args)
        {  
            List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
          
            long count = numbers.parallelStream().filter(i -> i>0).count();
            
            System.out.println("Positive count: " + count);
        }
    


二、Stream源码

package java.util.stream;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;

public interface Stream<T> extends BaseStream<T, Stream<T>> {

	//filter()方法的参数为Predicate(函数式接口)对象,用它进行过滤
    Stream<T> filter(Predicate<? super T> predicate);

	//map()方法的参数为Function(函数式接口)对象,map()方法将流中的所有元素用Function对象进行运算,生成新的流对象(流的元素类型可能改变)
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

    //summaryStatistics()方法进行数值统计。其实summaryStatistics()方法并不是Stream接口的方法,而是Stream API采用mapToInt()、mapToLong()、mapToDouble()三个方法分别生成IntStream 、LongStream 、DoubleStream 三个接口类型的对象,这个方法的参数分别为3个函数式接口ToIntFunction、ToLongFunction、ToDoubleFunction,使用时可以用lambda表达式计算返回对应的int、long、double类型即可。IntStream 、LongStream 、DoubleStream 三个接口类型都有一个summaryStatistics()方法,方法对应的类型分别是:IntSummaryStatistics、LongSummaryStatistics 、DoubleSummaryStatistics ,都有诸如统计数量、最大值、最小值、求和、平均值等方法(方法名和返回类型可能不同),利用这些方法我们可以方便的进行数值统计,列如:
    //    public static void main(String[] args)
    //    List numbers = Arrays.asList(-1, -2, 0, 4, 5);
    //    IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
    //    System.out.println("Max : " + stats.getMax());
    //    System.out.println("Min : " + stats.getMin());
    //    System.out.println("Sum : " + stats.getSum());
    //    System.out.println("Average : " + stats.getAverage());
    //    System.out.println("Count : " + stats.getCount());}
    IntStream mapToInt(ToIntFunction<? super T> mapper);
    LongStream mapToLong(ToLongFunction<? super T> mapper);
    DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

	//跟map()方法不同的是,Function函数的返回值类型是Stream类型,而不是R类型,即Function函数返回一个Stream流,这样flatMap()能够将一个二维的集合映射成一个一维的集合,比map()方法拥有更高的映射深度(此处可能有一点绕,可结合例子理解)
    //有一个字符串数组:List list = Arrays.asList("1 2", "3 4", "5 6");
    //其有三个元素,每个元素有两个数组并用空格隔开,如果每个元素以空格分割成2个元素,并遍历打印这6个元素,
	//用flatMap()方法如下:
	//list.stream().flatMap(item -> Arrays.stream(item.split(" "))).forEach(System.out::println);
    //而用map()方法:
	//list.stream().map(item -> Arrays.stream(item.split(" "))).forEach(n ->n.forEach(System.out::println));
    //可见,用map()方法,返回了一个“流中流”,需要在每个Stream元素遍历时,再加一层forEach进行遍历。
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

    IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
    LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
    DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);

	//去重,筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
    Stream<T> distinct();
	
    //sorted()--自然排序(Comparable)
    Stream<T> sorted();

	//sorted(Comparator com)--定制排序(Comparator),匿名类部类,列如:
    //final Stream sorted = personList.stream().sorted((p1, p2) -> {
    //    if (p1.getAge().equals(p2.getAge())) {
    //        return p1.getName().compareTo(p2.getName());
    //    } else {
    //        return p1.getAge().compareTo(p2.getAge());
    //    }
    //});
    //sorted.forEach(System.out::println);
    Stream<T> sorted(Comparator<? super T> comparator);

	//生成一个包含原Stream的所有元素的新Stream,并指定消费函数。peek()方法提供Consumer(消费)函数,但执行peek()方法时不会执行Consumer函数,而是等到流真正被消费时(终端操作时才进行消费)才会执行,这种操作为中间操作
    Stream<T> peek(Consumer<? super T> action);

	//截断流,使其元素不超过给定对象
    Stream<T> limit(long maxSize);

  	//跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
    Stream<T> skip(long n);

	//forEach()方法的参数为一个Consumer(消费函数,一个函数式接口)对象,forEach()方法用来迭代流中的每一个数据
    void forEach(Consumer<? super T> action);


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


    Object[] toArray();


    <A> A[] toArray(IntFunction<A[]> generator);

	// reduce操作又称为折叠操作,用于将流中的所有值合成一个。。reduce()方法参数为BinaryOperator类型的累加器(它接受两个类型相同的参数,返回值类型跟参数类型相同),返回一个Optional对象。实际上,Stream API中的mapToInt()方法返回的IntStream接口有类似的 average()、count()、sum()等方法就是做reduce操作,类似的还有mapToLong()、mapToDouble() 方法。当然,我们也可以用reduce()方法来自定义reduce操作。例如我们用reduce()方法来进行整数数组求和操作:
    //    public static void main(String[] args) {
    //    List numbers = Arrays.asList(-1, -2, 0, -1, 4, 5, 1);
    //    Integer total = numbers.stream().reduce((t, n) -> t + n).get();
    //    System.out.println("Total: " + total);}
    T reduce(T identity, BinaryOperator<T> accumulator);


    Optional<T> reduce(BinaryOperator<T> accumulator);


    <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

	//collect()方法的参数为一个java.util.stream.Collector类型对象,可以用java.util.stream.Collectors工具类提供的静态方法来生成,Collectors类实现很多的归约操作,如Collectors.toList()、Collectors.toSet()、Collectors.joining()(joining适用于字符串流)等。看一个简单示例:
    //用map()方法生成新的流,再用collect()方法返回原数组的绝对值数组。
    //    public static void main(String[] args) {  
    //    List numbers = Arrays.asList(-1, -2, 0, 4, 5);
    //    List abss = numbers.stream().map( n -> Math.abs(n)).collect(Collectors.toList());
    //    System.out.println("Abs list: " + abss);}
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

   
    <R, A> R collect(Collector<? super T, A, R> collector);

  	//返回流中最小值,列如年龄最小的人信息:
    //Optional minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
    Optional<T> min(Comparator<? super T> comparator);

 	//返回流中最大值,列如年龄最大的人信息:
    //Optional maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
    Optional<T> max(Comparator<? super T> comparator);

	//返回流中元素的总个数,列如:
    //List numbers = Arrays.asList(-1, -2, 0, 4, 5);  
    //long count = numbers.parallelStream().filter(i -> i>0).count();
    long count();

	//检查是否至少匹配一个元素,列如:
    boolean anyMatch(Predicate<? super T> predicate);
	
    //检查是否匹配所有元素,列如是否都是成年人:
    //boolean adult = personList.stream().allMatch(p -> p.getAge() >= 18);
    boolean allMatch(Predicate<? super T> predicate);

 	//检查是否没有匹配所有元素,列如:
    boolean noneMatch(Predicate<? super T> predicate);

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

	//返回当前流中的任意元素,列如:
    Optional<T> findAny();


    public static<T> Builder<T> builder() {
        return new Streams.StreamBuilderImpl<>();
    }


    public static<T> Stream<T> empty() {
        return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
    }


    public static<T> Stream<T> of(T t) {
        return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
    }


    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
        return Arrays.stream(values);
    }


    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {
            @SuppressWarnings("unchecked")
            T t = (T) Streams.NONE;

            @Override
            public boolean hasNext() {
                return true;
            }

            @Override
            public T next() {
                return t = (t == Streams.NONE) ? seed : f.apply(t);
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iterator,
                Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
    }


    public static<T> Stream<T> generate(Supplier<T> s) {
        Objects.requireNonNull(s);
        return StreamSupport.stream(
                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
    }


    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
        Objects.requireNonNull(a);
        Objects.requireNonNull(b);

        @SuppressWarnings("unchecked")
        Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
        Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
        return stream.onClose(Streams.composedClose(a, b));
    }


    public interface Builder<T> extends Consumer<T> {
        @Override
        void accept(T t);

        default Builder<T> add(T t) {
            accept(t);
            return this;
        }

        Stream<T> build();
    }
}

三、Stream常见操作

//常见的中间操作方法(filter、limit、skip、distinct、map、flatMap、sorted、peek)
import lombok.Data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TestStreamAPI {

    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岁的人并输出;
        personList.stream().filter((p) -> p.getAge() > 18).forEach(System.out::println);

        System.out.println("-------------------------------------------");

        // 2)找出所有中国人的数量
        long chinaPersonNum = personList.stream().filter((p) -> p.getCountry().equals("中国")).count();
        System.out.println("中国人有:" + chinaPersonNum);

        // limit
        personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
        System.out.println();
        // skip
        personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);

        // distinct
        personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);

        // map
        personList.stream().map((p) -> {
            PersonCountry personName = new PersonCountry();
            personName.setCountry(p.getCountry());
            return personName;
        }).distinct().forEach(System.out::println);

        // map2
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");

        final Stream<Stream<Character>> streamStream
                = list.stream().map(TestStreamAPI::getCharacterByString);
//        streamStream.forEach(System.out::println);
        streamStream.forEach(sm -> sm.forEach(System.out::print));

        // flatMap
        final Stream<Character> characterStream = list.stream().flatMap(TestStreamAPI::getCharacterByString);
        characterStream.forEach(System.out::print);

        // sort
        final Stream<Person> sorted = personList.stream().sorted((p1, p2) -> {

            if (p1.getAge().equals(p2.getAge())) {
                return p1.getName().compareTo(p2.getName());
            } else {
                return p1.getAge().compareTo(p2.getAge());
            }
        });
        sorted.forEach(System.out::println);

        // allMatch
        final Stream<Person> stream = personList.stream();
        final boolean adult = stream.allMatch(p -> p.getAge() >= 18);
        System.out.println("是否都是成年人:" + adult);

        final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
        System.out.println("是否都是中国人:" + chinaese);

        // max min
        final Optional<Person> maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
        System.out.println("年龄最大的人信息:" + maxAge.get());

        final Optional<Person> minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
        System.out.println("年龄最小的人信息:" + minAge.get());

        // reduce
        List<Integer> integerList = new ArrayList<>(100);
        for(int i = 1;i <= 100;i++) {
            integerList.add(i);
        }
        final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
        System.out.println("结果为:" + reduce);

        final Optional<Integer> totalAge = personList.stream().map(Person::getAge).reduce(Integer::sum);
        System.out.println("年龄总和:" + totalAge);

        // collect
        final List<String> collect = personList.stream().map(p -> p.getCountry()).distinct().collect(Collectors.toList());
        System.out.println(collect);

        final Double collect1 = personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
        System.out.println("平均年龄为:" + collect1);

        final Optional<Integer> maxAge2 = personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compareTo));
        System.out.println(maxAge2.get());

        try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
            final Optional<Integer> minAge2 = integerStream.collect(Collectors.minBy(Integer::compareTo));
            System.out.println(minAge2.get());
        }
    }

    public static Stream<Character> getCharacterByString(String str) {

        List<Character> characterList = new ArrayList<>();

        for (Character character : str.toCharArray()) {
            characterList.add(character);
        }

        return characterList.stream();
    }
}

@Data
class PersonCountry {
    private String country;
}

@Data
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;
    }
}

你可能感兴趣的:(Java,java)