preface

If some friends have not used java8 Stream before this kind of chained programming way to do development, want to learn. If some of your friends have only learned part of the usage, they want to learn more. If some of you want to see if there’s a good example that works in real life. So congratulations, this article is perfect for you.

First, let’s look at the stream inheritance:

The parent interface of Stream, IntStream, LongStream, DoubleStream is BaseStream. BaseStream’s four subinterface methods are all the same, except that IntStream, LongStream, and DoubleStream store the basic types directly, avoiding automatic loading and unpacking and making it more efficient. However, we actually use Stream a little more.

Let’s look at the stream workflow diagram:

Why do you want to learn stream chain programming

Business requirement 1: Specify an array of strings, find the same elements, and count the number of repetitions.

We used to do something like this:

public class CountTest {

    @Test
      public void testCount1(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");

        Map<String, Long> countMap = new HashMap<>();
        for (String data : list) {
            Long aLong = countMap.get(data);
            if (Objects.isNull(aLong)) {
                countMap.put(data, 1L);
            } else {
                countMap.put(data, ++aLong);
            }
        }

        countMap.forEach((key, value) -> System.out.println("key:" + key + " value:"+ value)); }}Copy the code

Execution Result:

key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1
Copy the code

Let’s see what we can do with a stream from java8:

public class CountTest {

    @Test
    public void testCount2(a) {
     List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
        Map<String, Long> countMap =         list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        countMap.forEach((key, value) ->         System.out.println("key:" + key + " value:"+ value)); }}Copy the code

Execution Result:

key:a value:3
key:ab value:2
key:b value:1
key:bd value:1
key:abc value:2
key:abcd value:1
Copy the code

We can see that testCount1 and testCount2 execute the same result with only one line of code:

Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Copy the code

You can implement the multi-line logic in testCount1 above.

Business requirement 2: Find if the specified string exists from a specified string array

We used to do something like this:

public class FindTest { @Test public void testFind1() { String findStr = "bd"; List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc"); boolean match = false; for (String data : list) { if (data.equals(findStr)) { match = true; break; }} // Result: match:true system.out.println ("match:" + match); }}Copy the code

Let’s see what we can do with a stream from java8:

public class MatchTest { @Test public void testFind2() { String findStr = "bd"; List<String> list = Lists.newArrayList("a", "b", "ab", "abc", "a", "ab", "a", "abcd", "bd", "abc"); boolean match = list.stream().anyMatch(x -> x.equals(findStr)); Println ("match:" + match); // Result: match:true system.out.println ("match:" + match); }}Copy the code

We can see that calling testFind1 and testFind2 results in the same result. However, using Java8 Stream syntax, it’s nice to do it again in one line of code.

Java8 Stream super detailed usage guide

The stream operators are roughly divided into two types: intermediate operators and termination operators

Intermediate operation:

1.filter(T-> boolean)

Filter the data, retaining elements whose Boolean is true, and return a collection

public class FilterTest {
    @Test
    public void testFilter(a) {
        List<Integer> list = Lists.newArrayList(20.23.25.28.30.33.37.40);
        // Filter data sets greater than or equal to 30 from the specified data set
        List<Integer> collect = list.stream().filter(x -> x >= 30).collect(Collectors.toList());
        // Result: [30, 33, 37, 40]System.out.println(collect); }}Copy the code

Collect (Collectors. ToList ()) converts a stream to a List type. Collect is actually a termination operation.

2.map(T -> R)

Conversion operators that convert data, such as string to int, long, double, or one entity to another. Contains map, mapToInt, mapToLong, and mapToDouble

public class MapTest {


    @Test
    public void testMap(a) {
        List<String> list = Lists.newArrayList("1"."2"."3"."4"."5"."6");
        List<Long> collect1 = list.stream().map(x -> Long.parseLong(x)).collect(Collectors.toList());
        // Result: [1, 2, 3, 4, 5, 6]
        System.out.println(collect1);

        // Result: 111111
        list.stream().mapToInt(x -> x.length()).forEach(System.out::print);
        System.out.println("");

        // Result: 111111
        list.stream().mapToLong(x -> x.length()).forEach(System.out::print);
        System.out.println("");

        // Result: 1.01.01.01.01.01.0list.stream().mapToDouble(x -> x.length()).forEach(System.out::print); }}Copy the code

3.flatMap(T -> Stream)

Map each element T in the flow to a flow, and concatenate each flow into a flow

public class FlatMapTest {

    @Test
    public void testFlatMap(a) {
        List<List<String>>  list = new ArrayList<List<String>>(){{
            add(Lists.newArrayList("a"."b"."c"));
            add(Lists.newArrayList("d"."e"."f"));
            add(Lists.newArrayList("j"."k"."y"));
        }};
        // Results: [a, B, C], [D, e, f], [J, k, Y]]
        System.out.println(list);
        List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList());
        [a, b, C, D, e, f, j, k, y]System.out.println(collect); }}Copy the code

We can see that flatMap makes it easy to turn two-dimensional data from strings into one-bit arrays.

4.distinct

Deduplication, similar to the use of DISTINCT in MSQL, uses the equals method at the bottom for comparison.

public class DistinctTest {

    @Test
    public void testDistinct(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
        List<String> collect = list.stream().distinct().collect(Collectors.toList());
        // Result: [a, b, ab, ABC, abcd, bd]System.out.println(collect); }}Copy the code

There is another way to get rid of Collectors, and you can use it later. Collectors. ToSet ().

5.sorted

Order elements, provided you implement the Comparable interface, and of course you can customize comparators.

public class SortTest {

    @Test
    public void testSort(a) {
        List<Integer> list = Lists.newArrayList(5.3.7.1.4.6);
        List<Integer> collect = list.stream().sorted((a, b) -> a.compareTo(b)).collect(Collectors.toList());
        // Result: [1, 3, 4, 5, 6, 7]System.out.println(collect); }}Copy the code

6.limit

If there are 10 elements, select only the first 3 elements

public class LimitTest {

    @Test
    public void testLimit(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
        List<String> collect = list.stream().limit(3).collect(Collectors.toList());
        // Result: [a, b, ab]System.out.println(collect); }}Copy the code

7.skip

Skip operations, such as: there are 10 elements, starting from the fifth element to the next element

public class SkipTest {

    @Test
    public void testSkip(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
        List<String> collect = list.stream().skip(5).collect(Collectors.toList());
        // Result: [ab, a, abcd, bd, ABC]System.out.println(collect); }}Copy the code

8.peek

Pick out operation,

public class PeekTest {
    @Test
    public void testPeek(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
        // Result: abababcaabaabcdbdabclist.stream().peek(x -> x.toUpperCase()).forEach(System.out::print); }}Copy the code

Sharp-eyed friends will notice that the x.topperCase () conversion to uppercase doesn’t actually work. Try changing peek to map:

public class PeekTest {
    @Test
    public void testPeek(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."a"."ab"."a"."abcd"."bd"."abc");
        // Result: ABABABCAABAABCDBDABClist.stream().map(x -> x.toUpperCase()).forEach(System.out::print); }}Copy the code

As you can see, conversion to uppercase works with the map operation, but not with peek. Peek only performs operations on elements in the Stream, but the data is not returned to the Stream, so the elements in the Stream remain the same.

Termination operation:

1.forEach

Traversal operations, including forEach and forEachOrdered

ForEach: supports parallel processing

ForEachOrdered: The order is processed and the traversal speed is slow

public class ForEachTest {

    @Test
    public void testForEach(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab");
        // Result: A, B, ab
        list.stream().forEach(x-> System.out.print(x+' '));
        System.out.println("");

        // Can be simplified
        // Result: A, B, ab
        list.forEach(x-> System.out.print(x+' '));
        System.out.println("");

        // Result: A, B, ab
        list.stream().forEachOrdered(x-> System.out.print(x+' ')); }}Copy the code

2.collect

Collectors, which collect all elements, provide many Collectors. Including toMap, toSet, toList, Joining, groupingBy, maxBy, and minBy.

ToMap: Transforms the data stream into a map that contains elements in the form of keys/values

ToSet: Converts a data stream to a set containing elements that cannot be repeated

ToList: Flow the data out as a list, which contains ordered elements

Joining: joining a string

GroupingBy: Groups a list into a map

Couting: Count elements

MaxBy: Gets the maximum element

MinBy: Gets the smallest element

summarizingInt: Summarize an element of type int, return IntSummaryStatistics, and call a specific method to count the elements: GetCount, getSum, getMin, getMax, getAverage

SummarizingLong: Element of type summarizingLong, same as summarizingInt

SummarizingDouble: A summarizingDouble element, same as summarizingInt

AveragingInt: Gets the average value of elements of type int and returns a double

AveragingLong: Gets the average value of elements of type long. Use the same as averagingInt

AveragingDouble: Gets the average value of elements of type double. Use the same way as averagingInt

Mapping: Gets a mapping that returns a portion of the original element as a new element

public class CollectTest {

    @Data
    @AllArgsConstructor
    class User {
        private String name;
        private Integer age;
    }


    @Test
    public void testCollect(a) {
        List<String> list0 = Lists.newArrayList("a"."b"."ab");
        Map<String, String> collect0 = list0.stream().collect(Collectors.toMap(String::new, Function.identity()));
        {ab=ab, a=a, b=b}
        System.out.println(collect0);

        List<String> list = Lists.newArrayList("a"."b"."ab"."a"."b"."ab");
        List<String> collect1 = list.stream().collect(Collectors.toList());
        // Result: [a, b, ab, a, b, ab]
        System.out.println(collect1);

        // result: [a, ab, b]
        Set<String> collect2 = list.stream().collect(Collectors.toSet());
        System.out.println(collect2);

        String collect3 = list.stream().collect(Collectors.joining(","));
        A,b,ab,a,b,ab
        System.out.println(collect3);

        Map<String, List<String>> collect4 = list.stream().collect(Collectors.groupingBy(Function.identity()));
        {ab=[ab, ab], a=[a, a], b=[b, b]}
        System.out.println(collect4);

        Long collect = list.stream().collect(Collectors.counting());
        // Result: 6
        System.out.println(collect);

        String collect5 = list.stream().collect(Collectors.maxBy((a, b) -> a.compareTo(b))).orElse(null);
        // result: b
        System.out.println(collect5);

        String collect6 = list.stream().collect(Collectors.minBy((a, b) -> a.compareTo(b))).orElse(null);
        // result: a
        System.out.println(collect6);

        List<String> list2 = Lists.newArrayList("2"."3"."5");
        IntSummaryStatistics summaryStatistics = list2.stream().collect(Collectors.summarizingInt(x -> Integer.parseInt(x)));
        long sum = summaryStatistics.getSum();
        // Result: 10
        System.out.println(sum);

        Double collect7 = list2.stream().collect(Collectors.averagingInt(x -> Integer.parseInt(x)));
        // Result: 3.333333333335
        System.out.println(collect7);

        List<User> userList = new ArrayList<User>() {{
            add(new User("jack".23));
            add(new User("james".30));
            add(new User("curry".28));
        }};
        List<String> collect8 = userList.stream().collect(Collectors.mapping(User::getName, Collectors.toList()));
        //[jack, james, curry]System.out.println(collect8); }}Copy the code

3.find

Search operations, including findFirst and findAny

FindFirst: Finds the first one and returns type Optional

FindAny: The first element is found using stream(), and one of the elements is found using parallelStream(), and the type returned is Optional

public class FindOpTest {

    @Test
    public void testFindOp(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab"."abc"."bc"."ab");
        // Find the first matched element
        String data1 = list.stream().findFirst().orElse(null);
        // result: a
        System.out.println(data1);

        String data2 = list.stream().findAny().orElse(null);
        // result: aSystem.out.println(data2); }}Copy the code

4.match

Matching operations, including allMatch, anyMatch, and noneMatch

AllMatch: Returns Boolean if all elements meet the criteria

AnyMatch: Returns Boolean if any element meets the condition

NoneMatch: All elements do not meet the criteria, return Boolean

public class MatchTest {

    @Test
    public void testMatch(a) {
        List<Integer> list = Lists.newArrayList(2.3.5.7);
        boolean allMatch = list.stream().allMatch(x -> x > 1);
        // Result: true
        System.out.println(allMatch);

        boolean allMatch2 = list.stream().allMatch(x -> x > 2);
        // Result: false
        System.out.println(allMatch2);

        boolean anyMatch = list.stream().anyMatch(x -> x > 2);
        // Result: true
        System.out.println(anyMatch);

        boolean noneMatch1 = list.stream().noneMatch(x -> x > 5);
        // Result: false
        System.out.println(noneMatch1);

        boolean noneMatch2 = list.stream().noneMatch(x -> x > 7);
        // Result: trueSystem.out.println(noneMatch2); }}Copy the code

5.count

The effect is similar to that of calling the size() method of the collection

public class CountOpTest {

    @Test
    public void testCountOp(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab");
        long count = list.stream().count();
        // Result: 3System.out.println(count); }}Copy the code

6. Min and Max

Min: Gets the minimum value and returns data of type Optional

Max: Gets the maximum value. Returns data of type Optional

public class MaxMinTest {

    @Test
    public void testMaxMin(a) {
        List<Integer> list = Lists.newArrayList(2.3.5.7);
        Optional<Integer> max = list.stream().max((a, b) -> a.compareTo(b));
        // Result: 7
        System.out.println(max.get());

        Optional<Integer> min = list.stream().min((a, b) -> a.compareTo(b));
        // Result: 2System.out.println(min.get()); }}Copy the code

7.reduce

Protocol operation, the value of the entire data stream is reduced to a value, count, min, Max is the underlying use of reduce.

The reduce operation enables the generation of a value from a Stream, not arbitrarily, but according to a specified calculation model.

public class ReduceTest {

    @Test
    public void testReduce(a) {
        List<Integer> list = Lists.newArrayList(2.3.5.7);
        Integer sum1 = list.stream().reduce(0, Integer::sum);
        // Result: 17
        System.out.println(sum1);

        Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);
        // Result: 17
        System.out.println(reduce.get());

        Integer max = list.stream().reduce(0, Integer::max);
        // Result: 7
        System.out.println(max);

        Integer min = list.stream().reduce(0, Integer::min);
        // Result: 0
        System.out.println(min);


        Optional<Integer> reduce1 = list.stream().reduce((a, b) -> a > b ? b : a);
        / / 2System.out.println(reduce1.get()); }}Copy the code

8.toArray

Array operations that convert elements of a data stream to arrays.

public class ArrayTest {

    @Test
    public void testArray(a) {
        List<String> list = Lists.newArrayList("a"."b"."ab");
        String[] strings = list.stream().toArray(String[]::new);
        // Result: A, B, ab
        for (int i = 0; i < strings.length; i++) {
            System.out.print(strings[i]+""); }}}Copy the code

A stream is a single pipe, called a stream, and is primarily used for the logical processing of collections.

ParallelStream: Multiple pipes that provide parallel processing of streams. ParallelStream is another important feature of streams, and its underlying implementation is implemented using the Fork/Join framework

public class StreamTest {

    @Test
    public void testStream(a) {
        List<Integer> list = Lists.newArrayList(1.2.3.4.5.6.7);
        // Result: 1234567list.stream().forEach(System.out::print); }}Copy the code
public class ParallelStreamTest {
    @Test
    public void testParallelStream(a) {
        List<Integer> list = Lists.newArrayList(1.2.3.4.5.6.7);
        // Result: 5726134list.parallelStream().forEach(System.out::print); }}Copy the code

We can see that forEach is used directly by parallelStream to traverse data out of order.

What if you want parallelStream traversals to be sequential?

public class ParallelStreamTest {

    @Test
    public void testParallelStream(a) {
        List<Integer> list = Lists.newArrayList(1.2.3.4.5.6.7);
        // Result: 1234567list.parallelStream().forEachOrdered(System.out::print); }}Copy the code

ParallelStream works by:

A case study in practice

1. Find the same element in both sets. This mode is used to import data in batches. Query data before adding or modifying data in batches.

public class WorkTest {

    @Test
    public void testWork1(a) {
        List<String> list1 = Lists.newArrayList("a"."b"."ab");
        List<String> list2 = Lists.newArrayList("a"."c"."ab");
        List<String> collect = list1.stream()
                .filter(x -> list2.stream().anyMatch(e -> e.equals(x)))
                .collect(Collectors.toList());
        // Result: [a, ab]System.out.println(collect); }}Copy the code

2. Set a and B, filter out the elements in set A but not in set B. This situation can be used if you specify an ID set, query the data set from the database according to the ID set, and filter out the ids that do not exist in the data set according to the ID set, and these ids need to be added.

@Test
public void testWork2(a) {
    List<String> list1 = Lists.newArrayList("a"."b"."ab");
    List<String> list2 = Lists.newArrayList("a"."c"."ab");
    List<String> collect = list1.stream()
      .filter(x -> list2.stream().noneMatch(e -> e.equals(x)))
      .collect(Collectors.toList());
    // Result: [b]
    System.out.println(collect);
}
Copy the code

3. Filter data based on conditions and redo data transformations

  @AllArgsConstructor
  @Data
  class User {
      private String name;
      private Integer age;
  }

  @Test
  public void testWork3(a) {
      List<User> userList = new ArrayList<User>() {{
          add(new User("jack".23));
          add(new User("james".30));
          add(new User("curry".28));
          add(new User("tom".27));
          add(new User("sue".29));
      }};

      List<String> collect = userList.stream()
              .filter(x -> x.getAge() > 27)
              .sorted((a, b) -> a.getAge().compareTo(b.getAge()))
              .limit(2)
              .map(User::getName)
              .collect(Collectors.toList());
      [curry, Sue]
      System.out.println(collect);
  }
Copy the code

4. Count the age of the youngest person with the same name in the specified set

@Test
public void testWork4(a) {
    List<User> userList = new ArrayList<User>() {{
        add(new User("tom".23));
        add(new User("james".30));
        add(new User("james".28));
        add(new User("tom".27));
        add(new User("sue".29));
    }};

    userList.stream().collect(Collectors.groupingBy(User::getName))
            .forEach((name, list) -> {
                User user = list.stream().sorted((a, b) -> a.getAge().compareTo(b.getAge())).findFirst().orElse(null);
                // Result: name: Sue,age:29
                // name:tom,age:23
                // name:james,age:28
                System.out.println("name:" + name + ",age:" + user.getAge());
            });
}
Copy the code

One last word (attention, don’t fuck me for nothing)

If this article is of any help or inspiration to you, please pay attention to it. Your support is the biggest motivation for me to keep writing.

Ask for a key three even: like, forward, look.

In addition to pay attention to the public number: [Su SAN said technology], in the public number reply: interview, code artifact, development manual, time management have super good fan welfare, in addition reply: add group, can communicate with a lot of BAT big factory seniors and learn.