Java tamp foundation (24) - Stream API new features of Java8

1, Stream Profile

In addition to the introduction of Java8 easy to use Lambda expressions, Date API outside, in addition to one of the highlights is the Stream API, and most worthy of a knowledge point for all Java developers to learn because its function is very powerful, especially and learned previously Lambda expressions, functions, interface, method references when used together.

Stream is a data flow channel, the operation for the sequence of elements generated by the data source, i.e. the set of operations, like array. And wherein the difference between the set of stream are: a set of data is talking, while talking about the stream is calculated . Stream API for all java.util.stream located at such a package, it can help developers to carry out a series of operations on a collection from a higher level of abstraction, similar to using a SQL database query execution. And means java.util.stream packet, we can set of declarative expression of simplicity, the possible parallel processing arrays and other data sources. From the outside to realize iterative change the internal iteration. It contains efficient polymerization operation, processing large amounts of data, but also a number of built-in operation, including filtering, sorting, and so the polymerization. In short, use Stream to operate the collection - reduce the amount of code to enhance the readability and improve operational efficiency.

Then to mention here: Stream and Java IO in the stream are two things completely different concept. Is used herein to explain the stream can be set to various serial or concurrent objects gather operation, the Java IO stream to handle data transfer between the devices, both of them different.

Stream main features are:
. 1, Stream data itself is not stored, the corresponding data in the collection (set) in, or generated only when needed storage.
2, stream does not modify the data source, always returns a new stream.
3, stream operations are delayed (the lazy) to perform: performing only the final result only when needed, i.e., until the termination operation is performed.

2, in a conventional manner using a simple comparison Stream

First, we use the previous approach to the collection of some of the operations, the following sample code:

public class StreamTest {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        list.add("five");
        list.add("six");

        //遍历数据,只遍历4个
        for (int i = 0; i < list.size()-1; i++) {
                //过滤掉字符串——two
                if (!(list.get(i).contains("two"))) {
                    System.out.println(list.get(i));
            }
        }
    }
}

I believe that something like this java code above estimate that each developer every minute can be achieved. However, if it requires more complex, it will be a large increase in code amount, then it is stored by creating a set of data, and then traverse the collection, and then screened by specific conditions and outputs the result. This code is more or less there will be disadvantages:
1, the amount of code more than one, certainly more difficult to maintain. Readability will deteriorate.

2, difficult to extend, to modify the parallel processing is estimated to cost a lot of energy.

Then the following shows the use Stream to achieve the same functionality is very simple, if you need to add other methods is also very convenient. Note: some of the ways Stream might temporarily not clear, it does not matter, it will be introduced one by one behind them.

//使用filter,limit,forEach方法
list.stream().filter(s->!s.contains("two")).limit(4).forEach(System.out::println);

The final results of the two ways of running the code is the same:

image

From the above case where we can find Stream Stream be directly connected into a string, then the result set of the stream into the filter outputs and returns. As for what type of turn into, which were inferred from the JVM. Execution of the entire program is described in a like matter, without excessive intermediate variables, GC may be reduced pressure, to improve efficiency. So then we began to explore the Stream function.

3, the operation of the three steps Stream

Stream principle is: the element to be viewed as a streaming processing, streaming in the pipeline, and the pipeline may be processed on the node, including a filter, filtering, de-emphasis, sorting, aggregation. An intermediate element processed in the pipeline operation, the final result obtained by the preceding processing operation is terminated.

Stream so all operations are divided into three parts: Create Object -> intermediate operation -> termination operation, following FIG.

image

(1) Create Stream: a data source (such as: collection, arrays), obtaining a stream.

(2), an intermediate operation: a chain intermediate operation, the data of the data source is processed.

(3), terminate the operation (operation terminal): a termination operation, performing the intermediate operation chain, and produce results.

4. Create a Stream object

①, was extended using Java8 Collection interface, which provides a method of obtaining two streams:

  • default Stream <E> stream () returns a data stream
  • default Stream <E> parallelStream () Returns a parallel stream
        // 1, Method 1: Collection 
        the ArrayList <String> = new new List the ArrayList <> (); 
        // Get the order flow, i.e. sequential acquisition 
        Stream <String> Stream list.stream = (); 
        // Get a parallel stream , i.e., while the data fetch, disorder 
        Stream <String> stream1 = list.parallelStream ( );

②, use Java8 in Arrays Static method stream () Gets an array of stream:

  • public static <T> Stream <T> stream (T [] array) Returns a stream
        // 2, two methods: by the array 
        String STR [] = new new String [] { "A", "B", "C", "D", "E"}; 
        Stream <String> STREAM2 Arrays.stream = ( str);

③, static methods of the Stream class itself (), to create a flow value through explicit excipients, it can accept any number of parameters:

  • public static <T> Stream <T> of (T ... values) Returns a stream
        // 3, three methods: the static method Stream of () 
        Stream <Integer> = STREAM3 Stream.of (. 1, 2, 3,. 4,. 5,. 6);

④, create an unlimited flow (iteration generation)

  • public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
  • public static<T> Stream<T> generate(Supplier<T> s)
        // 4 way Four: Create an unlimited flow (iteration generation) 
        // iteration (need to pass a seed, which is the starting value, and then pass a unary operation) 
        Stream.iterate (2, (the X-) -> 2 * X) .limit (10) .forEach (the System.out :: the println); 
        // generates (generate infinite objects) 
        Stream.generate (() -> Math.random ()) limit (10) .forEach (. System.out :: println);

More than three in four ways you can learn, the fourth is not commonly used. In addition to these there are other ways to create additional Stream object above, as Stream.empty (): Creates an empty stream, Random.ints (): random number stream, and so on.

5, the intermediate operation Stream

All operations will return to the middle of the operation of a new flow, but it does not modify the original data source, and the operation is the delay in the implementation of (lazy), meaning that only the middle of the operation really started when the start of the operation is terminated.

Intermediate operation mainly in the following method: filter, limit, skip, distinct, map (mapToInt, flatMap etc.), sorted, peek, parallel, sequential, unordered like.

For a better example, let's create a Student class:

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

    public Student() { }

    public Student(int id, String name, int age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id &&
                age == student.age &&
                Objects.equals(name, student.name) &&
                Objects.equals(address, student.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, address);
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}

All the test data following this set of data with the following (will change if necessary) will be used:

        List <Student> = Students Arrays.asList ( 
                new new Student (1, "Joe Smith", 18, "Beijing"), 
                new new Student (2, "John Doe," 19, "Shanghai"), 
                new new Student (3 " Wang Wu, "20," Guangzhou "), 
                new new Student (4," Zhao six ", 21," Zhejiang "), 
                new new Student (5," Sun seven ", 22," Shenzhen ") 
        );

Intermediate stream is then introduced start operation method:

①, filter (Predicate <super T?> Predicate): Filter: receiving Lambda expressions, showing certain elements from the stream by filtration.

// screening older than 19 years old and lives in Zhejiang students 
students.stream () filter (s -> s.getAge ()> 19). .Filter (s -> " Zhejiang" .equals (s.getAddress ()) ) .forEach (System.out :: println);

Operating results as follows:

image

Here we create five students object, and then screened through a filter to screen out older than 19 years of age and home address is a collection of students in Zhejiang.

 

②, limit (long maxSize): cut-off: it represents no more than a given number of elements.

        // Get 3 data given 
        students.stream () limit (3) .forEach (System.out :: println).;

operation result:

image

We just let it cut three, every time start capturing data from the first.

 

③, skip (long n): Skip: means skip the first n elements. If the flow is less than n number of elements, an empty stream. It limit (n) is complementary.

        3 // skip before data 
        students.stream () skip (3) .forEach (System.out :: println).;

operation result:

image

It can be found exactly complementary data output limit method.

 

④, distinct (): Screening: removing duplicate elements streams.

Here I will first and second data into a different, see what happens.

    static void main public (String [] args) { 
        List <Student> = Students. Arrays.asList ( 
                new new Student (. 1, "San", 18, "Beijing"), 
                new new Student (. 1, "San", 18, "Beijing"), 
                new new Student (3, "Wang Wu," 20, "Guangzhou"), 
                new new Student (4, "Zhao six", 21, "Zhejiang"), 
                new new Student (5, "Sun seven", 22 "Shenzhen") 
        ); 
        // remove duplicate elements 
        students.stream () DISTINCT () forEach (System.out :: println);.. 

    } 
}

operation result:

image

The same elements are found to be removed, but note: DISTINCT entity needs to override hashCode () and equals () methods can be used.

 

⑤, map (Function mapper <super T, extends R??>): Mapping (conversion): one type of stream is converted to another type of flow.

For ease of understanding we analyze, the need to pass a map function Function <T, R> interface to achieve the object function, and apply the abstract interface receives the methods of the T type, R is a return type, it can also be understood as a map Mapping relations.

image

        // lambda expression 
        students.stream () the Map (S-> s.getName ()) forEach (System.out :: println);.. 
        // method reference 
        students.stream () map (Student :: getName ). .forEach (System.out :: println);

operation result:

image

The above example will be converted to ordinary Student object String object, acquires the name of the Student object.

 

⑥, flatMap (Function <super T, extends Stream <extends R >> mapper???): Conversion combined: the value of each stream are converted into another stream, then all the streams are combined into one stream and returns.

It is somewhat similar and map. Upon receiving flatMap Stream, each element will be received by the Stream Stream taken out into another, the last one comprising a plurality of return elements Stream.

image

        List<Student> student1= Arrays.asList(
                new Student(1, "张三", 18, "北京"),
                new Student(2, "李四", 19, "上海")
        );

        List<Student> student2= Arrays.asList(
                new Student(3, "王五", 20, "广州"),
                new Student(4, "赵六", 21, "浙江"),
                new Student(5, "孙七", 22, "深圳")
        );

        //lambda
        Stream.of(student1,student2).flatMap(student -> student.stream()).forEach(System.out::println);
        //方法引用
        Stream.of(student1,student2).flatMap(List<Student>::stream).forEach(System.out::println);
        //常规方法
        Stream.of(student1,student2).flatMap(new Function<List<Student>, Stream<?>>() {
            @Override
            public Stream<?> apply(List<Student> students) {
                return students.stream();
            }
        }).forEach(System.out::println);

运行结果都是:

image

因为flatMap中最后需要将每个元素组合成一个流,所以flatMap方法形参返回了一个stream流。

如果map和flatmap还不清楚可以参考这篇博客:java8 stream流操作的flatMap(流的扁平化) 写的很清楚。

 

⑦、sorted():自然排序:按自然顺序排序的流元素。

这里就不用Student对象作为举例了,否则要在Student类中实现Comparable接口。

        //自然排序
        List<String> list = Arrays.asList("CC", "BB", "EE", "AA", "DD");
        list.stream().sorted().forEach(System.out::println);

运行结果:

image

上面使用String中默认实现的接口自动完成排序。

 

⑧、sorted(Comparator<? super T> comparator):自定排序:按提供的比较符排序的流元素 。

        //自定排序
        students.stream().sorted((s1,s2)-> {
                    if(s1.getId()>s2.getId()){
                        return s1.getId().compareTo(s2.getId());
                    }else{
                        return -s1.getAge().compareTo(s2.getAge());
                    }
                }).forEach(System.out::println);

运行结果:

image

上面的代码表示为先按照id进行排序,如果id相同则按照年龄降序排序,你可以将其他id改为 1测试一下Open-mouthed smile

6、Stream终止操作

终止操作主要有以下方法:forEach、 forEachOrdered、anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、count、min、 max、 reduce、 collect、toArray、iterator。下面只介绍一下常用的方法。

注意:Stream流只要进行了终止操作后,就不能再次使用了,再次使用得重新创建流。

①、void forEach(Consumer<? super T> action):遍历:接收Lambda表达式,遍历流中的所有元素。

forEach上面已经用的够多的了,这里就不说了。

 

②、anyMatch/allMatch/noneMatch:匹配:返回值为boolean,参数为(Predicate<? super T> predicate)。

  • allMatch——检查是否匹配所有元素
  • anyMatch——检查是否至少匹配一个元素
  • noneMatch——检查是否没有匹配的元素
        boolean b = students.stream().allMatch((s) -> s.getAge() > 20);
        System.out.println("allMatch()"+"\t"+b);

        boolean b1 = students.stream().anyMatch((s) -> s.getAge() > 21);
        System.out.println("anyMatch()"+"\t"+b1);

        boolean b2 = students.stream().noneMatch((s) -> s.getAge() == 22);
        System.out.println("noMatch()"+"\t"+b2);

运行结果:

image

 

③、findFirst/findAny:查找:返回Optional<T>,无参数。

  • findFirst——返回第一个元素
  • findAny——返回当前流中的任意元素
        //先按id排好序
        Optional<Student> first = students.stream().sorted((s1, s2) -> s1.getId().compareTo(s2.getId())).findFirst();
        System.out.println("findFirst()"+"\t"+first.get());

        Optional<Student> any = students.stream().filter((s)->s.getAge()>19).findAny();
        System.out.println("findAny()"+"\t"+any.get());

运行结果:

image

 

④、long count():统计:返回流中元素的个数。

        //统计流中数量
        long count = students.stream().count();
        System.out.println("count()"+"\t"+count);

运行结果:

image

 

⑤、max/min:大小值:返回Optional<T>,无参数。

  • max——返回流中最大值
  • min——返回流中最小值
        //获取年龄最大值
        Optional<Student> max = students.stream().max((s1, s2) -> Integer.compare(s1.getAge(), s2.getAge()));
        System.out.println("max()"+"\t"+max.get());
        //获取年龄最小值
        Optional<Student> min = students.stream().min((s1, s2) -> Integer.compare(s1.getAge(), s2.getAge()));
        System.out.println("min()"+"\t"+min.get());

运行结果:

image

 

⑥、reduce:归约:可以将流中元素反复结合在一起,得到一个值。

  • T reduce(T identity, BinaryOperator<T> accumulator)
  • Optional<T> reduce(BinaryOperator<T> accumulator)
        //累加0-10
        List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        //1、T reduce(T identity, BinaryOperator<T> accumulator);
        System.out.println("reduce1--"+sum);

        //2、Optional<T> reduce(BinaryOperator<T> accumulator);
        Optional<Integer> sum1 = list.stream().reduce(Integer::sum);
        System.out.println("reduce2--"+sum1.get());

        Optional<Integer> sum2 = list.stream().reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer1, Integer integer2) {
                return Integer.sum(integer1,integer2);
            }
        });
        System.out.println("reduce3--"+sum2.get());

运行结果:

image

备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。

7、Collectors的使用

Stream流与Collectors中的方法是非常好的搭档,通过组合来以更简单的方式来实现更加强大的功能,我们利用Stream中的collect方法来实现。

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

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

所以下面逐一介绍Collectors中的方法:

①、Collectors.toList():将流转换成List。

        /**
         * Collectors.toList():将流转换成List
         */
        List<String> list = students.stream().map(student -> student.getName()).collect(Collectors.toList());
        System.out.println("toList----"+list);

 

②、Collectors.toSet():将流转换为Set。

        /**
         * Collectors.toSet():将流转换成Set
         */
        Set<String> set = students.stream().map(student -> student.getName()).collect(Collectors.toSet());
        System.out.println("toSet----"+set);

 

③、Collectors.toCollection():将流转换为其他类型的集合。

        /**
         * Collectors.toCollection():将流转换为其他类型的集合
         */
        TreeSet<String> treeSet = students.stream().map(student -> student.getName()).collect(Collectors.toCollection(TreeSet::new));
        System.out.println("toCollection----"+treeSet);

 

④、Collectors.counting():元素个数。

        /**
         * Collectors.counting():元素个数
         */
        Long aLong = students.stream().collect(Collectors.counting());
        System.out.println("counting----"+aLong);

 

⑤、Collectors.averagingInt()、Collectors.averagingDouble()、Collectors.averagingLong():求平均数。

这三个方法都可以求平均数,不同之处在于传入得参数类型不同,但是返回值都为Double。

        /**
        *  Collectors.averagingInt()
        *  Collectors.averagingDouble()
        *  Collectors.averagingLong()
        *  求平均数
        */
        Double aDouble = students.stream().collect(Collectors.averagingInt(student -> student.getAge()));
        System.out.println("averagingInt()----"+aDouble);

        Double aDouble1 = students.stream().collect(Collectors.averagingDouble(student -> student.getAge()));
        System.out.println("averagingDouble()----"+aDouble1);

        Double aDouble2 = students.stream().collect(Collectors.averagingLong(student -> student.getAge()));
        System.out.println("averagingLong()----"+aDouble2);

 

⑥、Collectors.summingDouble()、Collectors.summingDouble()、Collectors.summingLong():求和。

这三个方法都可以求和,不同之处在于传入得参数类型不同,返回值为Integer, Double, Long。

        /**
         *  Collectors.summingInt()
         *  Collectors.summingDouble()
         *  Collectors.summingLong()
         *  求和
         */
        Integer integer = students.stream().collect(Collectors.summingInt(student -> student.getAge()));
        System.out.println("summingInt()----"+integer);

        Double aDouble3 = students.stream().collect(Collectors.summingDouble(student -> student.getAge()));
        System.out.println("summingDouble()----"+aDouble3);

        Long aLong1 = students.stream().collect(Collectors.summingLong(student -> student.getAge()));
        System.out.println("summingLong()----"+aLong1);

 

⑦、Collectors.maxBy():求最大值。

        /**
         * Collectors.maxBy():求最大值
         */
        Optional<Integer> integer1 = students.stream().map(student -> student.getId()).collect(Collectors.maxBy((x, y) -> Integer.compare(x, y)));
        System.out.println("maxBy()----"+integer1.get());

 

⑧、Collectors.minBy():求最小值。

        /**
         * Collectors.minBy():求最小值
         */
        Optional<Integer> integer2 = students.stream().map(student -> student.getId()).collect(Collectors.minBy((x, y) -> Integer.compare(x, y)));
        System.out.println("maxBy()----"+integer2.get());

 

 

⑨、Collectors.groupingBy():分组 ,返回一个map。

        /**
         * Collectors.groupingBy():分组 ,返回一个map
         */
        Map<Integer, List<Student>> listMap = students.stream().collect(Collectors.groupingBy(Student::getId));
        System.out.println(listMap);

其中Collectors.groupingBy()还可以实现多级分组,如下:

        /**
         * Collectors.groupingBy():多级分组 ,返回一个map
         */

        //先按name分组然后得出每组的学生数量,使用重载的groupingBy方法,第二个参数是分组后的操作
        Map<String, Long> stringLongMap = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting()));
        System.out.println("groupingBy()多级分组----"+stringLongMap);

        //先按id分组然后再按年龄分组
        Map<Integer, Map<Integer, List<Student>>> integerMapMap = students.stream().collect(Collectors.groupingBy(Student::getId, Collectors.groupingBy(Student::getAge)));
        System.out.println("groupingBy()多级分组----"+integerMapMap);

        //遍历
        Map<Integer,List<Student>> map = new HashMap<>();
        Iterator<Map.Entry<Integer, Map<Integer, List<Student>>>> iterator = integerMapMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<Integer, Map<Integer, List<Student>>> entry = iterator.next();
            System.out.println("key="+entry.getKey()+"----value="+entry.getValue());
        }

 

⑩、Collectors.partitioningBy():分区。按true和false分成两个区。

        /**
         * Collectors.partitioningBy():分区,分成两个区
         */
        Map<Boolean, List<Student>> listMap1 = students.stream().collect(Collectors.partitioningBy(student -> student.getAge() > 18));
        System.out.println(listMap1);

⑪、Collectors.joining():拼接。按特定字符将数据拼接起来。

        /**
         * Collectors.joining():拼接
         */
        String str = students.stream().map(student -> student.getName()).collect(Collectors.joining("---"));
        System.out.println(str);

 

8. Conclusion

The Java8 Stream provides very powerful, use them to make our collections operations with less code, faster traverse the collection. And in java.util.stream package there is a class Collectors, it is a good stream and a very good partner to a more simple way to achieve a more powerful through the combination. Although some basic operations described above are Stream, but as long as we spare no practice can be used flexibly, quickly it can be applied to practical applications.

Guess you like

Origin www.cnblogs.com/tang-hao-/p/11605281.html