Also look at the code of my colleagues do not understand? Super Stream flow operating position not learn about

Java 8 new series of articles index.

  1. Jdk14 to be out, you can not use the Optional elegant handle null pointer?
  2. Jdk14 be out, Jdk8 time to deal with posture do not know about?
  3. Also look at the code of my colleagues do not understand? Lambda expressions, functions, interfaces look

Foreword

We all know Lambda and Java Stream are two major highlights features 8, in the previous article has introduced Lambda knowledge, this was introduced to Java Stream flow operating 8. It is completely different from the java.io package Input / Output Stream, Stream stream is not large data processing in real time. The Stream flow is operating on a set of Java Enhanced 8 operating functions, focusing on the collection of a variety of efficient, convenient and elegant aggregation operations . By means of Lambda expressions, significantly improve programming efficiency and readability . Stream and provides parallel computing mode, you can simple write parallel code, can now give full play to the advantages of multi-core processing computer.

Before you can use Stream flow operation you should first understand the Lambda knowledge, if not understand, can refer to the article before: also look at the code of my colleagues do not understand? Lambda expressions, functions, interfaces look .

Introduction 1. Stream flow

Stream Unlike other collections framework, nor is it some kind of data structure, data will not be saved, but it is responsible for the relevant calculation, more like a high-level iterators. Before the iterator, then we must first traverse in the implementation of business operations, and now only need to specify what action, Stream implicitly traversal to perform and then make the desired operation. In addition Stream and iterators like only one-way process, like the Pentium Yangtze River water to a thing of the past.

Since Stream flow provides an inert computing and parallel processing capability of the data when using the parallel calculation will be automatically decomposed into multiple segments and then parallel processing, the final results will be summarized. So Stream operations that allow the program to run more efficient.

2. Stream flow concept

Stream flow is always used according to certain steps, you can use the following abstract process.

Data source (source) -> Data processing / conversion (intermedia) -> result of the processing (terminal)

2.1. Data Sources

数据源(source)That is the source of the data can be obtained through a variety of ways Stream data sources, here are a few common ways to obtain.

  • Collection.stream (); acquisition stream from the collection.
  • Collection.parallelStream (); obtained from a set of parallel streams.
  • Arrays.stream (T array) or Stream.of (); acquisition stream from the array.
  • BufferedReader.lines (); acquisition stream from the input stream.
  • IntStream.of (); static acquisition stream from the process.
  • Stream.generate (); self-generated stream

2.2 Data processing

数据处理/转换(intermedia)There may be a plurality of operation steps, this step is also referred to as intermedia(operation center). Anyway operation in this step, it returns is a new stream object, the original data will not be any change, and this step is a 惰性计算process, that method does not call only begin processing only in the real when to start collecting the results, in the middle before it takes effect, and if traversal is not completed, the desired result has been obtained to (such as access to first value), it will stop the walk, and returns the result. 惰性计算Can significantly improve operational efficiency.

Data processing demonstration.

@Test
public void streamDemo(){
    List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
    // 1. 筛选出名字长度为4的
    // 2. 名字前面拼接 This is
    // 3. 遍历输出
    nameList.stream()
            .filter(name -> name.length() == 4)
            .map(name -> "This is "+name)
            .forEach(name -> System.out.println(name));
}
// 输出结果
// This is Jack
// This is Poul复制代码

数据处理/转换NATURAL filter operation not only demonstrated above filterand mapmapping two kinds, in addition to map (mapToInt, flatMap etc.), filter, distinct, sorted, peek, limit, skip, parallel, sequential, unordered like.

2.3. The results were collected

结果处理(terminal )Is the last step of the process stream, after completing this step flow will be completely exhausted, the flow can not continue to operate. Only to this operation, when flow 数据处理/转换and other intermediate calculation process will start, which is mentioned above 惰性计算. 结果处理It must have been the final step in a flow operation.

Result of the processing operations have common forEach, forEachOrdered, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny, iterator like.

The following example demonstrates a simple result of the process.

/**
 * 转换成为大写然后收集结果,遍历输出
 */
@Test
public void toUpperCaseDemo() {
    List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
    List<String> upperCaseNameList = nameList.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
    upperCaseNameList.forEach(name -> System.out.println(name + ","));
}
// 输出结果
// DARCY,CHRIS,LINDA,SID,KIM,JACK,POUL,PETER,复制代码

2.4. short-circuiting

There is a Stream operation is called short-circuiting, which means that when flow Stream infinite but need to return Stream flow is limited when desired but it can in a limited time calculated the results, then the operation is called short-circuiting. E.g. findFirst operation.

3. Stream flow using

Stream flow is always in use when operated by means of a Lambda expression, the operation flow Stream there are many ways, are commonly used include the following 11 kinds of operations.

3.1. Stream flow acquisition

Acquired in several ways Stream Stream data source in the above has been introduced, the following is an example of the use of acquisition for several Stream flow described above.

@Test
public void createStream() throws FileNotFoundException {
    List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
    String[] nameArr = {"Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter"};
    // 集合获取 Stream 流
    Stream<String> nameListStream = nameList.stream();
    // 集合获取并行 Stream 流
    Stream<String> nameListStream2 = nameList.parallelStream();
    // 数组获取 Stream 流
    Stream<String> nameArrStream = Stream.of(nameArr);
    // 数组获取 Stream 流
    Stream<String> nameArrStream1 = Arrays.stream(nameArr);
    // 文件流获取 Stream 流
    BufferedReader bufferedReader = new BufferedReader(new FileReader("README.md"));
    Stream<String> linesStream = bufferedReader.lines();
    // 从静态方法获取流操作
    IntStream rangeStream = IntStream.range(1, 10);
    rangeStream.limit(10).forEach(num -> System.out.print(num+","));
    System.out.println();
    IntStream intStream = IntStream.of(1, 2, 3, 3, 4);
    intStream.forEach(num -> System.out.print(num+","));
}复制代码

3.2. forEach

forEach is an important method Strean stream, used to traverse Stream flow, which supports a standard incoming Lambda expressions. However, it can not traverse the return / break for termination. It is also a terminaloperation, the data stream after the execution Stream will be consumed.

The output object.

List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numberList.stream().forEach(number -> System.out.println(number+","));
// 输出结果
// 1,2,3,4,5,6,7,8,9,复制代码

3.3. map / flatMap

Using mapthe one to one mapping the object into another object or form.

/**
 * 把数字值乘以2
 */
@Test
public void mapTest() {
    List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    // 映射成 2倍数字
    List<Integer> collect = numberList.stream()
            .map(number -> number * 2)
            .collect(Collectors.toList());
    collect.forEach(number -> System.out.print(number + ","));
    System.out.println();

    numberList.stream()
            .map(number -> "数字 " + number + ",")
            .forEach(number -> System.out.println(number));
}
// 输出结果
// 2,4,6,8,10,12,14,16,18,
// 数字 1,数字 2,数字 3,数字 4,数字 5,数字 6,数字 7,数字 8,数字 9,复制代码

The above mapdata can be mapped one to one, and in some cases may be more than 1 to 1 relationship so simple, there may be more than one pair. Then you can use the flatMap。下面演示use flatMapthe flat objects is expanded.

/**
 * flatmap把对象扁平化
 */
@Test
public void flatMapTest() {
    Stream<List<Integer>> inputStream = Stream.of(
            Arrays.asList(1),
            Arrays.asList(2, 3),
            Arrays.asList(4, 5, 6)
    );
    List<Integer> collect = inputStream
            .flatMap((childList) -> childList.stream())
            .collect(Collectors.toList());
    collect.forEach(number -> System.out.print(number + ","));
}
// 输出结果
// 1,2,3,4,5,6,复制代码

3.4. filter

Use filterfilters data, pick out the elements you want to, the following examples demonstrate how to pick out even numbers.

/**
 * filter 数据筛选
 * 筛选出偶数数字
 */
@Test
public void filterTest() {
    List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    List<Integer> collect = numberList.stream()
            .filter(number -> number % 2 == 0)
            .collect(Collectors.toList());
    collect.forEach(number -> System.out.print(number + ","));
}复制代码

The following results.

2,4,6,8,复制代码

3.5. findFirst

findFirstYou can find the first element of Stream stream, it returns a type Optional, if not know the usefulness Optional class, before you can refer to the article Jdk14 be out, it can not use the Optional elegant handle null pointer? .

/**
 * 查找第一个数据
 * 返回的是一个 Optional 对象
 */
@Test
public void findFirstTest(){
    List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    Optional<Integer> firstNumber = numberList.stream()
            .findFirst();
    System.out.println(firstNumber.orElse(-1));
}
// 输出结果
// 1复制代码

findFirstAfter method to find the required data will return no longer traverse the data, and therefore findFirstthe method can be operated Stream flows have unlimited data, it can be said that findFirstan short-circuitingoperation.

3.6. collect / toArray

Stream flow can easily be converted to other structures, the following are some common examples.

 /**
 * Stream 转换为其他数据结构
 */
@Test
public void collectTest() {
    List<Integer> numberList = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5);
    // to array
    Integer[] toArray = numberList.stream()
            .toArray(Integer[]::new);
    // to List
    List<Integer> integerList = numberList.stream()
            .collect(Collectors.toList());
    // to set
    Set<Integer> integerSet = numberList.stream()
            .collect(Collectors.toSet());
    System.out.println(integerSet);
    // to string
    String toString = numberList.stream()
            .map(number -> String.valueOf(number))
            .collect(Collectors.joining()).toString();
    System.out.println(toString);
    // to string split by ,
    String toStringbJoin = numberList.stream()
            .map(number -> String.valueOf(number))
            .collect(Collectors.joining(",")).toString();
    System.out.println(toStringbJoin);
}
// 输出结果
// [1, 2, 3, 4, 5]
// 112233445
// 1,1,2,2,3,3,4,4,5复制代码

3.7. limit / skip

Obtaining the first n elements or toss

/**
 * 获取 / 扔掉前 n 个元素
 */
@Test
public void limitOrSkipTest() {
    // 生成自己的随机数流
    List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
    ageList.stream()
            .limit(3)
            .forEach(age -> System.out.print(age+","));
    System.out.println();
    
    ageList.stream()
            .skip(3)
            .forEach(age -> System.out.print(age+","));
}
// 输出结果
// 11,22,13,
// 14,25,26,复制代码

3.8. Statistics

Mathematical statistics, find the maximum value of a set of arrays, the minimum number, and data, averages and the like.

/**
 * 数学计算测试
 */
@Test
public void mathTest() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
    IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics();
    System.out.println("最小值:" + stats.getMin());
    System.out.println("最大值:" + stats.getMax());
    System.out.println("个数:" + stats.getCount());
    System.out.println("和:" + stats.getSum());
    System.out.println("平均数:" + stats.getAverage());
}
// 输出结果
// 最小值:1
// 最大值:6
// 个数:6
// 和:21
// 平均数:3.5复制代码

3.9. groupingBy

Packet aggregation, and database functions Group by consensus.

/**
 * groupingBy
 * 按年龄分组
 */
@Test
public void groupByTest() {
    List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
    Map<String, List<Integer>> ageGrouyByMap = ageList.stream()            
        .collect(Collectors.groupingBy(age -> String.valueOf(age / 10)));
    ageGrouyByMap.forEach((k, v) -> {
        System.out.println("年龄" + k + "0多岁的有:" + v);
    });
}
// 输出结果
// 年龄10多岁的有:[11, 13, 14]
// 年龄20多岁的有:[22, 25, 26]复制代码

3.10. partitioningBy

/**
 * partitioningBy
 * 按某个条件分组
 * 给一组年龄,分出成年人和未成年人
 */
public void partitioningByTest() {
    List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
    Map<Boolean, List<Integer>> ageMap = ageList.stream()
            .collect(Collectors.partitioningBy(age -> age > 18));
    System.out.println("未成年人:" + ageMap.get(false));
    System.out.println("成年人:" + ageMap.get(true));
}
// 输出结果
// 未成年人:[11, 13, 14]
// 成年人:[22, 25, 26]复制代码

3.11 Advanced - own generation Stream stream

/**
 * 生成自己的 Stream 流
 */
@Test
public void generateTest(){
    // 生成自己的随机数流
    Random random = new Random();
    Stream<Integer> generateRandom = Stream.generate(random::nextInt);
    generateRandom.limit(5).forEach(System.out::println);
    // 生成自己的 UUID 流
    Stream<UUID> generate = Stream.generate(UUID::randomUUID);
    generate.limit(5).forEach(System.out::println);
}

// 输出结果
// 793776932
// -2051545609
// -917435897
// 298077102
// -1626306315
// 31277974-841a-4ad0-a809-80ae105228bd
// f14918aa-2f94-4774-afcf-fba08250674c
// d86ccefe-1cd2-4eb4-bb0c-74858f2a7864
// 4905724b-1df5-48f4-9948-fa9c64c7e1c9
// 3af2a07f-0855-455f-a339-6e890e533ab3复制代码

In the above example Stream flow is unlimited, but the obtained result is limited, using the Limitquantity obtained, so this operation is short-circuitingoperation.

4. Stream Flow advantages

4.1. Simple elegance

Proper use and proper formatting of Stream flow operation code is not only simple and elegant , more fun to watch. Coding style under the same operation when using the following comparative Stream Stream flow and without flow.

/**
 * 使用流操作和不使用流操作的编码风格对比
 */
@Test
public void diffTest() {
    // 不使用流操作
    List<String> names = Arrays.asList("Jack", "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");
    // 筛选出长度为4的名字
    List<String> subList = new ArrayList<>();
    for (String name : names) {
        if (name.length() == 4) {
            subList.add(name);
        }
    }
    // 把值用逗号分隔
    StringBuilder sbNames = new StringBuilder();
    for (int i = 0; i < subList.size() - 1; i++) {
        sbNames.append(subList.get(i));
        sbNames.append(", ");
    }
    // 去掉最后一个逗号
    if (subList.size() > 1) {
        sbNames.append(subList.get(subList.size() - 1));
    }
    System.out.println(sbNames);
}
// 输出结果
// Jack, Jill, Nate, Kara, Paul复制代码

If the Stream stream operation.

// 使用 Stream 流操作
String nameString = names.stream()
       .filter(num -> num.length() == 4)
       .collect(Collectors.joining(", "));
System.out.println(nameString);复制代码

4.2. Lazy evaluation

Mentioned above, 数据处理/转换(intermedia)the operating map (mapToInt, flatMap etc.), filter, distinct, sorted, peek, limit, skip, parallel, sequential, unordered and other such operations, and will not immediately call a method call, but actually use time to take effect, which would allow the operation to the time delay really need to use.

The following example will illustrate this.

 /**
  * 找出偶数
  */
 @Test
 public void lazyTest() {
     // 生成自己的随机数流
     List<Integer> numberLIst = Arrays.asList(1, 2, 3, 4, 5, 6);
     // 找出偶数
     Stream<Integer> integerStream = numberLIst.stream()
             .filter(number -> {
                 int temp = number % 2;
                  if (temp == 0 ){
                     System.out.println(number);
                 }
                 return temp == 0;
             });

     System.out.println("分割线");
     List<Integer> collect = integerStream.collect(Collectors.toList());
 }复制代码

If not 惰性计算, then obviously will first output even, then output 分割线. The actual effect yes.

分割线
2
4
6复制代码

Visible 惰性计算to calculate the delay to the time actually needed.

4.3. Parallel Computing

Stream can be used when obtaining the flow parallelStreammethod instead of streama method for parallel processing streams, parallel processing may be full advantage of multi-core, but without increasing the complexity of the coding.

The following code shows the ten million generated random number, the random number is multiplied by 2 for each summation, difference and time-consuming calculation of serial and parallel computing.

  /**
  * 并行计算
  */
 @Test
 public void main() {
     // 生成自己的随机数流,取一千万个随机数
     Random random = new Random();
     Stream<Integer> generateRandom = Stream.generate(random::nextInt);
     List<Integer> numberList = generateRandom.limit(10000000).collect(Collectors.toList());

     // 串行 - 把一千万个随机数,每个随机数 * 2 ,然后求和
     long start = System.currentTimeMillis();
     int sum = numberList.stream()
         .map(number -> number * 2)
         .mapToInt(x -> x)
         .sum();
     long end = System.currentTimeMillis();
     System.out.println("串行耗时:"+(end - start)+"ms,和是:"+sum);

     // 并行 - 把一千万个随机数,每个随机数 * 2 ,然后求和
     start = System.currentTimeMillis();
     sum = numberList.parallelStream()
         .map(number -> number * 2)
         .mapToInt(x -> x)
         .sum();
     end = System.currentTimeMillis();
     System.out.println("并行耗时:"+(end - start)+"ms,和是:"+sum);
 }复制代码

To obtain the following output.

串行耗时:1005ms,和是:481385106
并行耗时:47ms,和是:481385106复制代码

The effect is obvious, the code is simple and elegant.

5. Stream Stream Recommended

5.1 to ensure the correct layout

From the above use case can be found using the code stream Stream operation is very simple, and more readable. However, if an incorrect layout, it will look very bad, such as code examples below the same function, multi-layers operate it, is not it something of a big head?

// 不排版
String string = names.stream().filter(num -> num.length() == 4).map(name -> name.toUpperCase()).collect(Collectors.joining(","));
// 排版
String string = names.stream()
        .filter(num -> num.length() == 4)
        .map(name -> name.toUpperCase())
        .collect(Collectors.joining(","));复制代码

5.1 ensure the purity of function

If you want your Stream flow results for the same operations every time are the same, then you have to ensure the purity of Lambda expressions, that is, the following highlights.

  • Lambda does not change any element.
  • Lambda does not depend on any elements that may change.

This points to ensure power function, etc. is very important, otherwise you program execution results may become difficult to predict, as the following example.

@Test
public void simpleTest(){
    List<Integer> numbers = Arrays.asList(1, 2, 3);
    int[] factor = new int[] { 2 };
    Stream<Integer> stream = numbers.stream()
            .map(e -> e * factor[0]);
    factor[0] = 0;
    stream.forEach(System.out::println);
}
// 输出结果
// 0
// 0
// 0复制代码

This paper has been uploaded to the code

https://github.com/niumoo/jdk-feature/blob/master/src/main/java/net/codingme/feature/jdk8/Jdk8Stream.java

<End>

Website: www.codingme.net
If you enjoyed this article, you can focus the public number, grow together.
No public attention can not respond to routine access to resources of the whole network hottest core knowledge of Java pick up & interview data.No public

Guess you like

Origin juejin.im/post/5dd1ec29518825431138380a