Lambda expression stream

Lambda expression

1.Lambda expression usage scenarios:

Lambda expressions can replace anonymous inner classes in most situations. Note that, as mentioned above, the application of Lambda is to cancel anonymous inner classes in most situations. (It can only replace the creation of anonymous inner classes for interfaces with only one abstract method, because the program needs to determine the implemented functions and create instances by itself.) [It can only be a functional interface, even if it has only one abstract method Neither does abstract class! ], which can greatly optimize the code structure and make the code more compact.

2.Basic use of Lambda expressions:

Basic syntax: (T …) -> { … }, where (T …) is the parameter list, { … } is the method body, and -> is the Lambda operator, pronounced as goes to.

Predicate<String> predict = (String val) -> {
  if(val.length()>5){
    return true;
    }else{
    return false;
   }
};

3.Simplification of Lambda syntax:

(1) Simplify parameter types. Parameter types can be omitted in the parameter list (all omissions must be omitted)

Predicate<String> predict = (val) -> {
  if(val.length()>5){
    return true;
    }else{
    return false;
   }
};

(2) When there is only one parameter, “( )” can be omitted

Predicate<String> predict = val -> {
  if(val.length()>5){
    return true;
    }else{
    return false;
   }
};

(3) When there is only one sentence to describe the method body, “{ }” can be omitted

Predicate<String> predict = val ->
  return val.length()>5?true:false;

(4) When there is only one sentence describing the method body and it is a return statement, “return” can be omitted

Predicate<String> predict = val -> val.length()>5?true:false;

4.Method reference of Lambda expression:

Usage scenarios: Sometimes we don’t have to implement the methods in the interface ourselves, but can directly use ready-made methods written by others. In this case, we can use Lambda method references. That is: using someone else’s method as an unimplemented method in the functional interface, so the corresponding requirements must be: the parameter list is the same, the return value is the same, and the method name is not required to be the same.

(1) Basic syntax:

Method owner::Method name means: the method owner of a static method is the class name, and the method owner of an instance method is the object.

Consumer<String> consumer = System.out::println;

(2) Construction method reference:

When a functional interface returns an instance, if it has no special function, it can be referenced directly using the constructor method. The basic syntax is: class name::new;

Supplier<Object> supplier = Object::new;

5. Closure problem of Lambda expression:

The problems that anonymous inner classes have in functions must also exist in Lambda. For example, if we use an external variable in an anonymous inner class in a method, the compiler will add final modification to this variable by default. Therefore, if you try to modify this variable below, an error will be reported: The value is of final type and cannot be changed.

Stream stream

1.Basic introduction to Stream:

The Stream feature introduced in Java 8 makes the code structure more compact, concise and easy to read; and the concurrency mode makes the code execution faster.

2. The composition of Stream: Source, Intermediate, Terminal.

(1) Stream construction (Source)

Use Stream to directly construct the stream: Stream.of(T…)

Stream<String> stream = Stream.of("as", "dsd", "kojj", "fsdd");

Use arrays to construct streams: Arrays.Stream(T[] arr)

String[] strArr = {"as", "dsd", "kojj", "fsdd"};
Stream<String> stream = Arrays.stream(strArr);

Convert collection to stream: Collection.stream()

String[] strArr = {"as", "dsd", "kojj", "fsdd"};
List<String> list = Arrays.asList(strArr);
Stream<String> stream = list.stream();

Convert file reading to stream: java.io.BufferedReader.lines()

List<String> output = reader.lines().
     flatMap(line -> Stream.of(line.split(REGEXP))).
     filter(word -> word.length() > 0).
     collect(Collectors.toList());

Build the stream yourself: Stream.generate(Supplier supplier)
Note: The flow implemented by Supplier is unlimited, either using short-circuiting operation or using limit to limit the flow.

Random random = new Random();
Stream<Integer> stream = Stream.generate(random::nextInt);
stream.limit(10).forEach(System.out::println);

Use Stream.iterator(T seed, Function fun) to construct a stream. Infinite streams require short-circuiting operations

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
Stream.iterate(0,val->val + 3).limit(10).forEach(System.out::println);

Note: For basic numerical types, there are corresponding IntStream, LongStream and DoubleStream. Generic qualification can also be used, but boxing and unboxing operations are time-consuming, so the given three numerical streams should be used first.

(2) Conversion operation (Intermediate) Commonly used conversion operations mainly include:

map (mapToInt, flatMap, etc.), filter, distinct, sorted, peek, limit, skip, parallel, sequential, unordered.

Example:

map(Function fun) one-to-one processing

List<String> output = wordList.stream().
    map(String::toUpperCase).
    collect(Collectors.toList());

** flatMap(Function fun) flattens the hierarchical structure and summarizes it into a stream**

Stream<List<Integer>> inputStream = Stream.of(
     Arrays.asList(1),
     Arrays.asList(2, 3),Arrays.asList(4, 5, 6)
 );
Stream<Integer> outputStream = inputStream.
    flatMap((childList) -> childList.stream());

filter(Predict predict) will leave those that test predict.test(T t) as true to form a new stream

List<String> output = reader.lines().
     flatMap(line -> Stream.of(line.split(REGEXP))).
filter(word -> word.length() > 0).
     collect(Collectors.toList());

(3) Termination operation (Terminal) Common terminal operations mainly include:

forEach, forEachOrdered, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny, iterator.

Example:

forEach(Consumer consumer) traverses operation elements

roster.stream()
 .filter(p -> p.getGender() == Person.Sex.MALE)
 .forEach(p -> System.out.println(p.getName()));

Note: Since Stream is a Terminal operation, you cannot use two Terminal operations in one Stream.

stream.forEach(element -> doOneThing(element));
stream.forEach(element -> doAnotherThing(element));

Correspondingly, there is peek (Consumer consumer) to perform similar operations, because peek is an Intermediate operation and will not terminate the stream.

Stream.of("one", "two", "three", "four")
  .filter(e -> e.length() > 3)
  .peek(e -> System.out.println("Filtered value: " + e))
  .map(String::toUpperCase)
  .peek(e -> System.out.println("Mapped value: " + e))
  .collect(Collectors.toList());

It should be noted here that the output of the program is:

Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR

Therefore, you can see from here that the order in which Stream executes elements is that each element is executed in turn, rather than the flow of each stage being executed as a whole.

Option findFirst() returns the first element in the stream, which is a Terminal and short-circuiting operation

public static int getNum(){
    Random random = new Random();
    Stream<Integer> stream3 = Stream.generate(random::nextInt);
    //stream3.findFirst().ifPresent(System.out::println);
    return stream3.limit(0).findFirst().orElse(-1);
}

It should be noted here that for this type of operation, the Optional packaging type is returned, and there are corresponding processing methods such as orElse(T t), ifPresent(Consumer consumer), etc. NPE problems can be prevented to a certain extent in program logic. Similar operations include: findAny, max/min, reduce, etc.

T reduce(T identity, BinaryOperator accumulator) combines elements iteratively. In fact, max/min, sum, and average are all encapsulated reduce operations

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
//Pay attention to the difference between adding and not adding the initial value of identity, it is very reasonable
Optional<Integer> reduce = stream3.limit(10).reduce(Integer::sum);
Integer sum = stream3.limit(10).reduce(0, Integer::sum);

limit/skip(Long n) limit flow conditions

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
int num = stream3.limit(10).skip(3).collect(Collectors.toList()).size();
System.out.println(num);

sorted(Comparator comparator) sorts the elements

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
stream3.limit(10).sorted((o1, o2) -> o2-o1).forEach(System.out::println);

min/max/distinct()

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
stream3.limit(10).distinct().max((o1, o2) -> o1-o2).ifPresent(System.out::println);

br.lines().flatMap(line -> Stream.of(line.split(" ")))
  .filter(word -> word.length() > 0)
  .map(String::toLowerCase)
  .distinct()
  .sorted()
  .collect(Collectors.toList());
br.close();
System.out.println(words);

(4) Short-circuiting operation (short-ciecuiting)

Common short circuit operations include:

anyMatch, allMatch, noneMatch, findFirst, findAny, limit examples:

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
boolean b = stream3.limit(10).noneMatch(val -> val > 0);
System.out.println(b);

(5) Use Collectors for grouping operations (groupingBy/partitioningBy)

The main function of the java.util.stream.Collectors class is to assist in various useful reduction operations, such as converting output to Collection and grouping Stream elements.

Collectors.groupingBy(Function classifier) groups according to Function conditions

Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
     limit(100).
     collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
     Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
     System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
 }

Collectors.partitioningBy(Predicate predicate) is divided into two groups according to predict conditions

Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
Map<Boolean, List<Integer>> collect = stream3.limit(10).collect(Collectors.partitioningBy(val -> val > 0));
Set<Boolean> keySet = collect.keySet();
keySet.forEach(item->collect.get(item).forEach(System.out::println));

Above, multi-threaded processing methods such as parallelStream() are used to improve efficiency, but parallel processing cannot guarantee that elements are ordered, and thread safety issues should also be paid attention to.