Summary of some usage examples of Stream in java8 (which will be updated later)

Stream API

example

If there is a demand, you need to process the dishes queried in the database:

  • Select dishes with less than 400 calories

  • Sort the selected dishes

  • Get the name of the dish after sorting

    Dish: dish java

    public class Dish {
        private String name;
        private boolean vegetarian;
        private int calories;
        private Type type;
    
        // getter and setter
    }
    

Past way

private List<String> beforeJava7(List<Dish> dishList) {
        List<Dish> lowCaloricDishes = new ArrayList<>();

        //1. Select dishes with less than 400 calories
        for (Dish dish : dishList) {
            if (dish.getCalories() < 400) {
                lowCaloricDishes.add(dish);
            }
        }

        //2. Sort the selected dishes
        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
            @Override
            public int compare(Dish o1, Dish o2) {
                return Integer.compare(o1.getCalories(), o2.getCalories());
            }
        });

        //3. Get the name of the dishes after sorting
        List<String> lowCaloricDishesName = new ArrayList<>();
        for (Dish d : lowCaloricDishes) {
            lowCaloricDishesName.add(d.getName());
        }

        return lowCaloricDishesName;
    }

java8

 private List<String> afterJava8(List<Dish> dishList) {
        return dishList.stream()
                .filter(d -> d.getCalories() < 400)  //Select dishes with less than 400 calories
                .sorted(comparing(Dish::getCalories))  //Sort by calories
                .map(Dish::getName)  //Extract dish name
                .collect(Collectors.toList()); //Convert to List
    }

Sudden addition of new demand

·Classify the dishes found in the database according to the type of dishes, and return a map > result

Past way

private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) {
    Map<Type, List<Dish>> result = new HashMap<>();

    for (Dish dish : dishList) {
        //Initialize if not present
        if (result.get(dish.getType())==null) {
            List<Dish> dishes = new ArrayList<>();
            dishes.add(dish);
            result.put(dish.getType(), dishes);
        } else {
            //Add if there is one
            result.get(dish.getType()).add(dish);
        }
    }

    return result;
}

java8

private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) {
    return dishList.stream().collect(groupingBy(Dish::getType));
}

How to generate flow

1. Generate by collection

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();

2. Generate by array

int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);

Via arrays The Stream method generates a Stream, and the Stream generated by this method is a numerical Stream [i.e. IntStream] rather than a Stream. In addition, the use of numerical flow can avoid unpacking in the calculation process and improve the performance. The Stream API provides mapToInt, mapToDouble and mapToLong to convert the object Stream [i.e. Stream] into the corresponding numerical Stream, and provides the boxed method to convert the numerical Stream into the object Stream

3. Generate by value

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

Generate a Stream through the of method of Stream, and generate an empty Stream through the empty method of Stream

4. Generated by file

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())

Through files The line method gets a stream, and each stream is a line in a given file

5. Generated by function

Two static methods, iterate and generate, are provided to generate streams from functions

Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);

The iterate method accepts two parameters. The first is the initialization value and the second is the function operation. Because the stream generated by the iterator is infinite, the stream is truncated by the limit method, and only five even numbers are generated

Stream<Double> stream = Stream.generate(Math::random).limit(5);

The generate method accepts a parameter. The method parameter type is Supplier, which provides a value for the flow. The stream generated by generate is also an infinite stream, so it is truncated by limit convection

Operation type of flow

1. Intermediate operation

A flow can be followed by zero or more intermediate operations. Its main purpose is to open the flow, make some degree of data mapping / filtering, and then return a new flow for the next operation. Such operations are inert. Only calling such methods does not really start the flow traversal. The real traversal needs to wait until the terminal operation

1.1 filter filtering

 List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
 Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

The filter method is used to filter conditions. The filter method parameter is a condition

Used in instances

List<Integer> giftIds = exchangeCommodities.stream()
                .filter(e -> e.getType() == 2) /*Only the others of type 2 are excluded*/
                .map(LuckExchangeCommodity::getRelationId)
                .collect(Collectors.toList());

1.2 distinct remove duplicate elements

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();

Rapid removal of duplicate elements by distinct method

1.3 limit returns the number of specified streams

 List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
 Stream<Integer> stream = integerList.stream().limit(3);

Specify the number of return streams through the limit method. The parameter value of limit must be > = 0, otherwise an exception will be thrown

1.4 skip skip elements in the stream

 List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
 Stream<Integer> stream = integerList.stream().skip(2);

Skip the elements in the stream through the skip method. The above example skips the first two elements, so the print result is 2,3,4,5. The parameter value of skip must be > = 0, otherwise an exception will be thrown

1.5 map stream mapping

List<String> stringList = Arrays.asList("Java 8", "Lambdas",  "In", "Action");
Stream<Integer> stream = stringList.stream().map(String::length);

The so-called flow mapping is to map the accepted element to another element

The mapping can be completed through the map method. In this example, the mapping of string \ - > integer is completed. In the previous example, the mapping of dish - > string is completed through the map method

1.6 flatMap stream conversion

List<String> wordList = Arrays.asList("Hello", "World");
List<String> strList = wordList.stream()
        .map(w -> w.split(" "))
        .flatMap(Arrays::stream)
        .distinct()
        .collect(Collectors.toList());

Converts each value in one stream to another

The return value of map (w \ - > w.split ("")) is stream < string [] >. If we want to get stream < string [] >, we can complete the conversion of stream < string [] > \ - > stream < string > through flatMap method

1.7 element matching correlation

Three matching methods are provided

1.7.1 allMatch matches all
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
  System.out.println("All values are greater than 3");
}
1.7.2 anyMatch matches one of them
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
  System.out.println("There is a value greater than 3");
}

Equivalent to

for (Integer i : integerList) {
  if (i > 3) {
      System.out.println("There is a value greater than 3");
      break;
  }
}

If there is a value greater than 3, it will print. This function is realized through anyMatch method in java8

1.7.3 none matches
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
  System.out.println("All values are less than 3");
}

2. Terminal operation

A stream has and can only have one terminal operation. When this operation is executed, the stream is closed and can no longer be operated. Therefore, a stream can only be traversed once. If you want to traverse, you need to generate a stream through the source data. Only when the terminal operation is executed can the flow traversal really begin

2.1 number of statistical elements

There are two ways

2.1.1 pass count
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();

Count the number of elements in the output stream by using the count method

2.1.2 counting
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().collect(counting());

The method of counting the number of elements is particularly useful when used in conjunction with collect

2.2 search

Two search methods are provided

2.2.1 find first
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();

Find the first element greater than three through the findFirst method and print it

2.2.2 findAny randomly finds one
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

Find and print one of the elements greater than three through findAny method. For internal optimization reasons, it ends when the first element satisfying greater than three is found. The result of this method is the same as that of findFirst method. findAny method is provided to make better use of parallel streams. findFirst method has more restrictions on parallelism [parallel streams will not be introduced in this article]

2.3 element combination reduce

2.3.1 summation
int sum = integerList.stream().reduce(0, (a, b) -> (a + b));

Method reference abbreviation

int sum = integerList.stream().reduce(0, Integer::sum);
//perhaps
int sum = menu.stream().mapToInt(Dish::getCalories).sum();

Reduce accepts two parameters, one with an initial value of 0 and a BinaryOperator accumulator to combine the two elements to produce a new value. In addition, the reduce method also has an overloaded method without an initial value

Via summingInt

int sum = menu.stream().collect(summingInt(Dish::getCalories));

If the data type is double or long, the summation is performed by summingDouble or summingLong methods

Through sum

int sum = menu.stream().mapToInt(Dish::getCalories).sum();
2.3.2 maximum and minimum
Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);

it's fine too

OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();

min gets the minimum value in the stream, max gets the maximum value in the stream, and the method parameter is Comparator comparator

You can also get the minimum and maximum values through minBy/maxBy

Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));

minBy gets the minimum value in the stream, maxBy gets the maximum value in the stream, and the method parameter is Comparator comparator

You can also obtain the minimum and maximum values through reduce

Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);

There are different methods for the same operation when calculating the sum, maximum and minimum values above. collect, reduce and min/max/sum methods can be selected. Min, Max and sum methods are recommended. Because it is the most concise and easy to read. At the same time, mapToInt converts the object stream into a numerical stream, avoiding the boxing and unpacking operations

2.3.3 averaging

Average through averaging int

double average = menu.stream().collect(averagingInt(Dish::getCalories));

If the data type is double or long, average through the methods of averagedouble and averagelong

2.3.4 summarizingInt

At the same time, sum, average, maximum and minimum

IntSummaryStatistics intSummaryStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
double average = intSummaryStatistics.getAverage();  //Get average
int min = intSummaryStatistics.getMin();  //Get minimum value
int max = intSummaryStatistics.getMax();  //Get maximum
long sum = intSummaryStatistics.getSum();  //Get sum

If the data type is double or long, use summarizingDouble or summarizingLong methods

2.4 foreach set traversal

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
integerList.stream().forEach(System.out::println);

2.5 return set

List<String> strings = menu.stream().map(Dish::getName).collect(toList());
Set<String> sets = menu.stream().map(Dish::getName).collect(toSet());

2.6 splicing elements by joining

String result = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));

By default, if the map method is not used to map the string returned by the spliced toString method, the method parameter of joining is the delimiter of the element. If it is not specified, the generated string will be a string, which is not readable

2.7 grouping by groupingBy

Map<Type, List<Dish>> result = dishList.stream().collect(groupingBy(Dish::getType));

In the collect method, pass in groupingBy for grouping. The method parameter of groupingBy is the classification function. You can also use groupingBy for multi-level classification through nesting

2.8 partitioning by

Partition is a special group. Its classification basis is true and false, so the returned results can be divided into two groups at most

Map<Boolean, List<Dish>> result = menu.stream().collect(partitioningBy(Dish :: isVegetarian))

Equivalent to

Map<Boolean, List<Dish>> result = menu.stream().collect(groupingBy(Dish :: isVegetarian))

Let's take a more obvious example

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));

The key of the return value is still Boolean, but its classification is based on the range. The partition is more suitable for classification based on the range

Used in instances

 Map<Integer, List<LuckExchangeCommodity>> exchangeMap = 
     exchangeCommodities.stream()
              			.collect(
     Collectors.groupingBy(
         LuckExchangeCommodity::getGameId /*By what*/
         ,LinkedHashMap::new /*Ordered? LinkedHashMap is ordered. The default is unordered*/
         ,Collectors.toList() /*What is the final replacement? It's usually this*/
     							));

2.9 sort

Positive order example

giftList.sort(Comparator.comparing(GetGiftExchangeVo::getWeight));

oneTasks.stream().sorted(Comparator.comparing(LuckTaskVo::getNum)).collect(Collectors.toList())

Flashback example

giftList.sort(Comparator.comparing(GetGiftExchangeVo::getWeight).reversed());

oneTasks.stream().sorted(Comparator.comparing(LuckTaskVo::getNum).reversed()).collect(Collectors.toList())

2.10 transfer to Map

Map<Integer, String> goodsTypeMap = goodsTypeDao.selectAll()
    .stream()        
    .collect(Collectors.toMap(GoodsType::getId, GoodsType::getName));
//Object problem
Map<Integer, String> goodsTypeMap = goodsTypeDao.selectAll()
    .stream()        
    .collect(Collectors.toMap(GoodsType::getId, goodType -> goodType));

Keywords: Java

Added by Pinkerbot on Tue, 08 Mar 2022 04:03:00 +0200