• Java8-Stream


    Java8-Stream

    1. Stream

    1.1 Stream介绍

    1.1.1 介绍

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

      Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

    • Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

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

    • 元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果

    1460000018919149

    1.1.2 Stream特点

    (1)数据源

    • 数据源可以是集合,数组,I/O channel,产生器generator等(Stream本身并不保存数据

    (2)支持聚合操作

    • 聚合操作:类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等

    (3)Pipelining

    • 中间操作都会返回流对象stream本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)

    (4)内部迭代

    • 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现

    (5)延迟执行

    • 中间的操作只是提供了一个操作逻辑,并记录,只有最终求值时才会执行传入的操作

    1.1.3 直接对集合遍历操作与使用Stream操作集合的对比

    (1)使用循环遍历

    • 如果是处理简单数据,进行简单操作,使用循环遍历集合,并没有什么不妥,但是如果是复杂的大型的数据使用循环不仅会使处理效率变得底下,而且会降低代码的可读性

      • 例如下面代码:查找符合条件的学生

        • 以下是通过遍历集合,通过条件判断来对符合条件的数据进行操作
        class Student implements Comparable<Student> {
            int id;
            String name;
            String className;
            int grade;
        
            public Student(int id, String name, String className, int grade) {...}
        
            @Override
            public String toString() {...}
        
            //使用成绩决定排名
            @Override
            public int compareTo(Student o) {
                return this.grade-o.grade;
            }
        }
        
        public class StreamTest {
            public static void main(String[] args) {
                LinkedList<Student> studentList = new LinkedList<Student>();
                studentList.add(new Student(1, "Alice", "1班", 100));
                studentList.add(new Student(2, "Bob", "2班", 85));
                studentList.add(new Student(3, "Coco", "2班", 46));
                studentList.add(new Student(4, "Dav", "3班", 95));
                studentList.add(new Student(5, "Elizabeth", "4班", 81));
                studentList.add(new Student(6, "Fermat", "4班", 69));
                studentList.add(new Student(7, "grace", "4班", 59));
                //选取4班,成绩大于60的人,和其他班成绩大于50的人
                //由于是链表,为了性能,这里使用iterator进行迭代
                Iterator<Student> iter = studentList.listIterator();
                while(iter.hasNext()){
                    Student cur = iter.next();
                    if("4班".equals(cur.className)){
                        if(cur.grade>60) {
                            System.out.println(cur);
                        }
                    }
                    else if(cur.grade>50){
                        System.out.println(cur);
                    }
                }
            }
        }
        
        • 这只是一个很简单的例子,如果说数据量很大或者涉及几种数据,而且条件设置地比较复杂,代码量以及执行效率会变得非常差

    (2)使用Stream操作

    • 以下是通过Stream处理集合实例

      studentList.stream().filter(s->{
          if("4班".equals(s.className)){
              return s.grade > 60;
          }else {
              return s.grade > 50;
          }
      }).forEach(s-> System.out.println(s));
      

    1.2 Stream使用

    1.2.1 使用流程

    (1)流程

    • 当使用一个流时,通常包括三个基本步骤

      graph LR 获取数据源-->数据转换 数据转换-->传入需要执行的操作 传入需要执行的操作-->操作集合获取想要的结果
    • 当一个Stream流执行完终结方法时,就不能再使用该对象了,再次使用会抛出(IllegalStateException

    1.2.2 获取Stream接口对象

    (1)Stream接口

    • Stream接口是定义在java.util.stream.Stream<T>

    • public interface Stream<T> extends BaseStream<T, Stream<T>> {
          ...
      }
      

    (2)Stream接口实现对象的获取方式

    • Collection实现的stream()parallelStream()接口方法,创建一个顺序流:

      • java8中将Collection接口中加入了stream()parallelStream()默认方法,用于获取Stream对象

        public interface Collection<E> extends Iterable<E> {
            ...
            default Stream<E> stream() {
                    return StreamSupport.stream(spliterator(), false);
        	}
            
            ...
            default Stream<E> parallelStream() {
                return StreamSupport.stream(spliterator(), true);
            }
            ...
        }
        
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream(); //获取一个顺序流
        Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
        
        • 对于Map类型集合可以先获取keyset,再获取键的Stream对象;或者先获取values(),再获取值的Stream对象;也可以获取entrySet(),再获取键值对Stream对象
    • Arrays.stream(T[] array)静态方法:

      • public static <T> Stream<T> stream(T[] array) {
        	return stream(array, 0, array.length);
        }
        
        public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive{
        	return StreamSupport.stream(spliterator(array,startInclusive,endExclusive), false);
        }
        
        String[] nums = new String[10];
        Stream<String> stream = Arrays.stream(nums);
        
    • Stream中的静态方法:

      • public static<T> Stream<T> of(T... values) {
        	return Arrays.stream(values);
        }
        
        • 此方法将通过数组对象获取Stream对象
      • 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> iterate(final T seed, final UnaryOperator<T> f) {...}
        
    • BufferedReader对象的lines() 方法

      • public Stream<String> lines(){...}
        
        BufferedReader reader = new BufferedReader(new FileReader("/user/sd/readme.md"));
        Stream<String> lineStream = reader.lines();
        
    • 使用 Pattern.splitAsStream() 方法,将字符串分隔成流

      • Pattern pattern = Pattern.compile(",");
        Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
        stringStream.forEach(System.out::println);
        

    参考

    1.2.3 延迟方法

    • 延迟方法:一般为传入某种函数式接口来表明需要进行的操作,其返回类型依旧为Stream类型,所以可以链式调用(该系列方法并没有执行操作,只是传入函数式接口表明需要进行的操作)

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

    • 该方法通过传入predicate接口对象,作为筛选条件

      List<String> personList = new ArrayList();
      personList.add("Alice");
      personList.add("Bob");
      personList.add("Coco");
      Stream<String> stream = personList.stream()
      stream.filter(s->s.startsWith("A")).forEach(System.out::println);//Alice
      

    (2)Stream map(Functionsuper <T, ? extends R> mapper)

    • 该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流

      Stream<String> stream = Stream.of("1", "2", "3");
      Stream<Integer> result = stream.map(Integer::parseInt);
      

    (3)Stream limit(long maxSize)

    • 该方法可以对流进行截取,只取用前n个

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

      stream.limit(2).forEach(System.out::println);//Alice Bob
      

    (4)Stream skip(long n)

    • 可以使用 skip 方法获取一个跳过n个数据后的新流

    • 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流

      stream.skip(1).forEach(System.out::println);//Bob Coco
      

    (5)Stream<T> distinct()

    • 该方法会将重复元素给去除掉

    • 该方法使用hashCode()equals(Object o)方法来比较元素,只有两者都相同才认为是相同的,只会保留第一个所遇到的元素

      public class StreamTest {
      
          public static void main(String[] args){
              List<String> list = new ArrayList();
              StreamTest s = new StreamTest();
              list.add("1");
              list.add("2");
              list.add("1");
              list.add(new String("1"));
              list.stream().distinct().forEach(System.out::println);//1 2
          }
      }
      
    • 当我们需要去重时也可以使用Set去重:

      public static void main(String[] args) {
          users.parallelStream().filter(distinctByKey(User::getId))
              .forEach(System.out::println);
      }
      
      
      public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
          Set<Object> seen = ConcurrentHashMap.newKeySet();
          return t -> seen.add(keyExtractor.apply(t));
      }
      
      • 通过Function接口处理数据后,将处理后的数据添加尝试添加进Set,如果添加不成功就代表已经有改元素了,返回false,此时过滤器函数会将改元素丢弃,实现去重

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

    • 该方法通过传入Function接口实例将数据元素进行元素转换,构造新的Stream对象

    • 此外还有方法XXStream mapToXX(ToXXFunction<? super T> mapper)等一系列方法,来将数据转换为特定类型

      List<String> list = new ArrayList();
      StreamTest s = new StreamTest();
      list.add("String");
      list.add("哈哈");
      list.add(new String("hello"));
      list.stream().map(String::toUpperCase).forEach(System.out::println);//STRING 哈哈 HELLO
      

    (7)Stream<T> sorted(Comparator<? super T> comparator)

    • 该方法使用传入的Comparator进行比较,然后排序,构建排序后的新Stream对象

    • 还包含一个无参的构造方法

      list.add("String");
      list.add("哈哈");
      list.add(new String("hello"));
      list.stream().sorted(String::compareTo).forEach(System.out::println);//String hello 哈哈
      

    (8)Stream<T> peek(Consumer<? super T> action)

    • 该方法通过传入Consumer接口对元素进行一些操作,并返回新的Stream对象

      class A{
          int var = 0;
      
          void display(){
              System.out.println(var);
          }
      }
      
      List<A> list = new ArrayList();
      StreamTest s = new StreamTest();
      list.add(new A());
      list.add(new A());
      list.add(new A());
      list.stream().peek(o->o.var = 1).forEach(A::display);
      

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

    • 该静态方法可以将两个流合并成一个新Stream对象

    • 该方法传入的流的泛型类型要一致

      stream = Stream.concat(stream,Stream.of("1","2"));
      stream.forEach(System.out::println);//Alice Bob Coco 1 2
      

    1.2.4 终结方法

    • 终止方法:表明已经要求通过之前传入的操作进行数据处理,并给出结果

    (1)void forEach(Consumersuper <T> action)

    • 该方法接收一个 Consumer 接口对象,会将每一个流元素交给该函数进行处理

      List<String> personList = new ArrayList();
      personList.add("Alice");
      personList.add("Bob");
      personList.add("Coco");
      personList.stream().forEach(s->{
          if(s.startsWith("A")){
              System.out.println(s);
          }
      });//打印以A开头的字符串
      

    (2)long count()

    • 该方法返回操作后的元素个数+

      System.out.println(stream.filter(s -> s.startsWith("A")).count());//1
      

    (3)Object[] toArray()

    • 该方法将数据转入到数组中,并返回

      List<String> list = new ArrayList();
      StreamTest s = new StreamTest();
      list.add("String");
      list.add("哈哈");
      list.add(new String("hello"));
      System.out.println(Arrays.toString(list.stream().sorted(String::compareTo).toArray()));//[String, hello, 哈哈]
      

  • 相关阅读:
    数据清洗SQL,一次性的工作
    PGSQL将字段改为不能为空
    C# WINFORM中splitcontainer调整列宽的方法
    【其他】etcd
    正则贪婪匹配
    MarsGIS for Cesium三维地图框架建设方案
    mysql 查询分区表中各个分区的数据量
    推送远程仓库(github/gitlab) 报错:Host key verification failed. fatal: Could not read from remote repository.
    sshkeygen t rsa C xxxx@xxxx.com解释
    DNS、CDN加速和域名解析之间的关系
  • 原文地址:https://www.cnblogs.com/nishoushun/p/12644545.html
Copyright © 2020-2023  润新知