Stream stream operation in Java

Keywords: Java Lambda Attribute

Preface

In recent internship, I saw some codes of my predecessors in the company, and found that there are many places worth learning. One part of them is to use Stream stream operation for collection, which is very beautiful and convenient. So learn some Stream streams and record them here.

What is Stream

Stream is a new feature in Java 8, which greatly enhances the function of collection objects and focuses on the convenient and efficient aggregation operation of collection objects. In addition, it can cooperate with Lambda expression to make the code easier to understand. In addition, stream provides two modes of operation, serial and parallel. Parallel operation can easily write high-performance concurrent programs.

Stream is like an advanced version of Iterator. With Iterator, we can only explicitly traverse one element and perform some operations on it. With stream, we only need to specify what operations to perform on the elements contained in the collection, such as "only get the user whose gender is male", "get the last name of each user" and so on. Stream will help us to complete the implicit traversal and conversion Data.

Unlike Iterator, Iterator can only operate serially, one element at a time and then the next. Stream supports serial and parallel operations, which rely on Java 7's Fork/Join framework (JSR166y) to split tasks and speed up processing.

Stream is like a pipeline. It is unidirectional and cannot be turned back. It can only be traversed once, and then it can no longer be used.

Using a Stream stream is generally divided into three steps: 1. Get data source - > 2. Intermediate - > 3. Terminal.

Intermediate operation: a flow can have 0 or more intermediate operations, such as data conversion, filtering and other operations, one after another. These operations are lazy, and the intermediate operation has not started the real traversal.

Terminal operation: a stream can only have one terminal operation. When terminal operation is used, the result will be returned. This stream can no longer be used. Only when the terminal operates can it really start to traverse.

In a Stream, the multiple intermediate operations of a Stream are not traversed every time. The intermediate operations are lazy, and the multiple intermediate operations are finally aggregated to the terminal operations. Only one cycle of traversal is performed. It can be understood that each intermediate operation is added to the terminal operation cycle as a judgment condition to complete the data conversion of each element.

The following are the classification of some Stream operation methods:

How to create a Stream

  • array

    1. Arrays.stream(T array);
    2. stream.of(array)
  • Collection
    1. Collection.stream()
    2. Collection.parallelStream()
  • BufferedReader
    1. java.io.BufferedReader.lines()
  • Static factory

    1. java.util.stream.IntStream.range()
    2. java.nio.file.Files.walk()
  • Build yourself
    1. java.util.Spliterator
  • Other
    1. Random.ints()
    2. BitSet.stream()
    3. Pattern.splitAsStream(java.lang.CharSequence)
    4. JarFile.stream()

Create example

//array
String[] array = new String[]{"1","2","3"};
Arrays.stream(array);
Stream.of(array);
Stream.of(1, 2, 3);
//aggregate
List<String> list = Arrays.asList(array);
list.stream();
list.parallelStream();
//Value, currently only three kinds of IntStream, LongStream and DoubleStream are supported
IntStream.of(new int[]{1,2,3}).forEach(System.out::println);
IntStream.range(1,3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);

Stream to other data structures

Stream stream = Stream.of("1","2","3");
//Array
String[] array2=(String[]) stream.toArray(String[]::new);
//Collection
List<String> list1=(List)stream.collect(Collectors.toList());
List<String> list2=(List)stream.collect(Collectors.toCollection(ArrayList::new));
Set set=(Set)stream.collect(Collectors.toSet());
Stack stack=(Stack)stream.collect(Collectors.toCollection(Stack::new));
//String
String str = stream.collect(Collectors.joining()).toString();

Classic usage

1. Take out an attribute of an element in a list as a list and filter it

List<Long> names=  users.stream().filter(Objects::nonNull).map(User::getId).collect(Collectors.toList());
//perhaps
List<Long> names1=
 users.stream().filter(Objects::nonNull).map(u->u.getId()).collect(Collectors.toList());
//Traversing list
names.forEach(System.out::println);

2. Convert List to Map

        //key:id value:name
        Map<Long, String> map = users.stream().collect(Collectors.toMap(p -> p.getId(), p -> p.getName()));
            //Or, the third parameter indicates that if the key keeps k1 repeatedly, k2 will be discarded.
        Map<Long, String> map2 = users.stream().collect(Collectors.toMap(User::getId,User::getName,(k1,k2)->k1));
        //key:id value:user
        Map<Long, User> map3 = users.stream().collect(Collectors.toMap(p -> p.getId(), p->p));
        //Traversal map, including k,v. map.values.forEach() cannot traverse Key
        map3.forEach((k,v)-> System.out.println("k:v="+k+":"+v));

3. Sort the List with sorted

//Descending, default is ascending
List<User> list=
    users.stream().sorted(Comparator.comparing(User::getId).reversed()).collect(Collectors.toList());
//Traversing list
list.forEach(System.out::println);

Comparator.comparing(User::getId) represents the data sorted by id.

4. Group the List into a Map

        //Group by gender
        Map<String,List<User>> map=users.stream().collect(Collectors.groupingBy(User::getSex));
        map.forEach((k,v)-> System.out.println("k:v="+k+":"+v));

5. Use map to convert upper case

List<String> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");
        list1.add("c");
        List<String> list2 = list1.stream().map(String::toUpperCase).collect(Collectors.toList());

6. flatMap and map

        //map
        List<String> str = Arrays.asList("a,b,c", "d,e", "f");
        List<String[]> list1 = str.stream().map(s -> s.split(",")).collect(Collectors.toList());
        list1.forEach(p-> System.out.print(Arrays.toString(p)+","));//[a, b, c],[d, e],[f]

        //flatMap
        List<String> list2 = str.stream().map(s -> s.split(",")).flatMap(Arrays::stream).sorted(Comparator.comparing(p->p.toString()).reversed()).collect(Collectors.toList());
System.out.println(list2);//[f, e, d, c, b, a]

The difference between flatMap and map is that flatMap transforms every element in a stream into a stream. After flatMap, we get the total set of elements in each stream. That is to say, we traverse each stream twice to extract the elements and merge them into the total set.

7. reduce

        //Sum sum=11, the first parameter 1 is the initial value (seed), and the second parameter is the binary operator. 1+1+2+3+4=11
        Integer sum = Stream.of(1, 2, 3, 4).reduce(1, Integer::sum);
        //concat="ABCD";
        String concat = Stream.of("A", "B", "C").reduce("", String::concat);
        //Sum. The reduce method has no initial value. The return type is Optional. You need to call the get() method to get a value.
        Integer sum2 = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
        //Take the maximum value, max=2.0.
        Double max = Stream.of(1.0, 2.0, -1.0, 1.5).reduce(Double.MIN_VALUE, Double::max);

optional is also a new feature in Java 8. It can store null or instance. Let's talk more about it.

8. limit and skip

List<Long> ids=users.stream().map(User::getId).limit(5).skip(2).collect(Collectors.toList());
ids.forEach(System.out::println);//1-10 outputs 2 3 4

limit(5) limits only the first five, skip(2) skips the first two. Especially, if limit and skip are used together with sorted, limit and skip should be used first.

Life goes on, learning goes on. Further efforts are needed. Twenty million one hundred and ninety-one thousand two hundred and ten

Posted by Dark_Archon on Tue, 10 Dec 2019 11:10:17 -0800