Java 8 functional programming

1、 Function interface

Why start with the function interface first? Because I think this is the entrance to java8 functional programming! Each function interface is annotated with @ functionalinterface. There is only one unimplemented method to receive lambda expressions. The significance of their existence is to package code blocks as data.

There is no need to interpret these function interfaces too much. They can be regarded as ordinary interfaces, but they have and only have one abstract method (because they want to receive lambda expressions).

@Functional interface this annotation forces javac to check whether an interface conforms to the standard of functional interface. If the annotation is added to an enumerated type, class, or another annotation, or if the interface contains more than one abstract method, javac will report an error.

2、 Lambda expressions and anonymous inner classes

Let's review the knowledge of anonymous inner classes:

 new Thread(new Runnable() {
     @Override
     public void run() {
         System.out.println(123);
     }
 }).start();
new Thread(()-> System.out.println(123)).start();

As mentioned above, unlike passing in an object that implements an interface, we passed in a code block - a function without a name. () is a parameter list, the same as in the anonymous inner class example above. - > Separate the parameters from the body of the lambda expression, which is some code that the operation will run later.

Lambda expressions simplify the writing of anonymous inner classes and omit function names and parameter types. That is, only the parameter name can be specified in the parameter list () without specifying the parameter type.

Java is a strongly typed language. Why not specify the parameter type? Thanks to the type inference mechanism of javac, the compiler can infer the type of the parameter according to the context information. Of course, when the inference fails, you need to manually specify the parameter type. The type inference mechanism of javac is as follows:

3、 Lambda expressions and collections

Java8 in Java The util package introduces a new class, stream java。 Before java8, when we iterated over a collection, we could only rely on the external iterator to serialize the collection. Stream supports collection order and parallel aggregation operations, and gives more control to collection classes. It is an internal iterative method. This is helpful for users to write simpler code and clarify what transformation to achieve, rather than how.

There are two kinds of stream operations. One is to describe the behavior of stream, such as filter, map, peek, etc., which will not produce results, which is called "lazy evaluation"; Another behavior, such as foreach and collect, that generates results from the stream is called "early evaluation".

Next, let's look at how stream combines lambda expressions to gracefully handle collections

1. Early evaluation

Collector: a generic structure that generates complex values from streams. Just pass it to the collect method and all streams can use it. In Java util. stream. Collectors provides some useful collectors. For example, tolist, toset, tomap, etc.

When a flow is created in an ordered set, the elements in the flow are arranged in the order of occurrence; If the set itself is disordered, the resulting stream is also disordered. It should be noted that the foreach method cannot guarantee that elements are processed in order. If you need to ensure that elements are processed in order, you should use the foreachordered method. Of course, we can use the sorted method to sort the elements in the stream.

Foreach / foreachordered - iteration set

list.forEach(e -> System.out.println(e));

map.forEach((k,v) -> {
    System.out.println(k);
    System.out.println(v);
});

Custom sorting of streams

List<String> collectSort = collect.stream().sorted(Comparator.comparing(String::length)).collect(Collectors.toList());

Allmatch, anymatch, nonematch - check whether the elements match

private boolean isPrime(int number) {
    return IntStream.range(2,number)
            .allMatch(x -> (number % x) != 0);
}

Collect (tolist()) - generate a list / set / custom collection from the values in the stream

List<String> list = Stream.of("java","C++","Python").collect(Collectors.toList());
等价于:
List<String> asList = Arrays.asList("java","Python");
Set<String> set = Stream.of("java","python","PHP").collect(Collectors.toSet());
TreeSet<String> treeSet = Stream.of("java","PHP").collect(Collectors.toCollection(() -> new TreeSet<>()));

Collect (tomap()) - generate a map collection from the values in the stream

When using tomap (), it should be noted that the key in the map cannot be repeated. If it is repeated, an exception will be thrown because the JVM does not know whether I want to use the new value or the old value? So the code is written as follows~~

Map<String,String> strMap = Stream.of("java","PHP").collect(Collectors.toMap(String::new,String::new,(oldValue,newValue) -> oldValue));

There is still a problem when using tomap() as described above, that is, if value is null, a NullPointerException will be reported, which can be solved as follows:

Map<Object,Object> mapResult = Stream.of("java","PHP").collect(HashMap::new,(map,str) -> map.put(str,str),HashMap::putAll);

Collect (maxby()), collect (minby()), collect (averaging int()) - evaluation operation

Optional<String> optionalMaxBy = Stream.of("java","PHP").collect(Collectors.maxBy(Comparator.comparing(str -> str.length())));
System.out.println(optionalMaxBy.get());

Optional<String> optionalMinBy = Stream.of("java","PHP").collect(Collectors.minBy(Comparator.comparing(str -> str.length())));
System.out.println(optionalMinBy.get());

Double aDouble = Stream.of("java","PHP").collect(Collectors.averagingInt(String::length));
System.out.println(aDouble);

Collect (collectors. Joining()) - string splicing operation

String joinStr = Stream.of("java","PHP").collect(Collectors.joining(",","[","]"));

The three parameters of joining () are separator, prefix and suffix.

Collect (partitioningby()), collect (groupingby()) - aggregate statistics operation

List<String> collect = Stream.of("java","PHP").collect(Collectors.toList());
// 数据分堆,按照 Boolean 值,将数据分成两堆
Map<Boolean,List<String>> listMap = collect.stream().collect(Collectors.partitioningBy(str -> str.equals("java")));

// 数据分组,有点像 sql 的 GROUP BY 用法,按照对象的某个属性分组
Map<Integer,List<String>> collectGroudBy = collect.stream().collect(Collectors.groupingBy(String::hashCode));

// 分组统计(类似sql分组统计) - 先将集合分组,然后统计分组的值 - 比如计算每个城市的姓氏集合-> 先按城市分组,再计算姓氏的集合。
Map<Integer,Long> longMap = collect.stream().collect(Collectors.groupingBy(String::length,Collectors.counting()));
Map<Integer,List<Integer>> listMap = collect.stream().collect(Collectors.groupingBy(String::length,Collectors.mapping(String::length,Collectors.toList())));

2. Inert evaluation

Range - cycle in steps of 1

private boolean isPrime(int number) {
    return IntStream.range(2,number)
            .allMatch(x -> (number % x) != 0);
}
等价于:
for (int i = 2; i < number ; i++) { ... }

Filter - iterates over and checks the elements in the filter

long count = list.stream().filter(x -> "java".equals(x)).count();

Distinct - element de duplication in convection

The de duplication of elements in the stream is based on the equal () method of the object. For streams with sequences, the first of the same elements shall prevail; For non sequential flows, the stability of de duplication is not guaranteed.

Stream.of("java","PHP","java").distinct().forEach(e -> System.out.println(e));

Findany, findfirst - returns the elements in a stream

Optional<String> first = Stream.of("java","PHP").findFirst();
Optional<String> any = Stream.of("java","PHP").findAny();

Map, maptoint, maptolong, maptodouble - converts the value in the stream to a new value

List<String> mapList = list.stream().map(str -> str.toUpperCase()).collect(Collectors.toList());

List<String> list = Stream.of("java","javascript","python").collect(Collectors.toList());
IntSummaryStatistics intSummaryStatistics = list.stream().mapToInt(e -> e.length()).summaryStatistics();
System.out.println("最大值:" + intSummaryStatistics.getMax());
System.out.println("最小值:" + intSummaryStatistics.getMin());
System.out.println("平均值:" + intSummaryStatistics.getAverage());
System.out.println("总数:" + intSummaryStatistics.getSum());

Maptoint, maptolong and maptodouble are similar to map operations, except that the return value of the function interface is changed to int, long and double.

Peek - process the elements in the stream one by one, with no return value

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());

The difference between peek and map is that peek accepts the higher-order function of consumer and has no return value; Map accepts function higher-order functions with return values.

Flatmap - connect multiple streams into one stream

List<String> streamList = Stream.of(list,asList).flatMap(x -> x.stream()).collect(Collectors.toList());

The related function interfaces of the flatmap method are the same as those of the map method, except that the return value of the method is limited to the stream type.

Reduce - aggregate operation, which generates a value from a group of elements. Sum (), max (), min (), count (), etc. are all reduce operations. They are set as functions only because they are commonly used

Integer sum1 = Stream.of(1,2,3).reduce(0,(acc,e) -> acc + e);

String maxStr = list.stream().max(Comparator.comparing(e -> e.length())).get();
String minStr = list.stream().min(Comparator.comparing(e -> e.length())).get();

The summation operation described above has two parameters: the initial value in the incoming stream and ACC. Add the two parameters. ACC is the accumulator, which saves the current accumulation result.

3. Parallel operation of stream

In Java 8, it's easy to write parallelized programs. Parallelizing the operation flow requires only one method call to be changed. If you already have a stream object, you can call its parallel method to make it have the ability of parallel operation. If you want to create a stream from a collection class, you can immediately get a stream with parallelism by calling parallelstream. At the bottom, parallel flows still follow the fork / join framework. Fork recursively decomposes the problem, and then each segment is executed in parallel. Finally, the join combines the results and returns the final value.

List<String> paraList = Stream.of("java","python").parallel().collect(Collectors.toList());
List<String> resultList = paraList.parallelStream().collect(Collectors.toList());

It should be noted that when you want to evaluate the stream, you cannot be in two modes at the same time, either parallel or serial. If both parallel and sequential methods are invoked at the same time, the last call method works.

The use of parallelized stream operations is to use operations to process large amounts of data. When dealing with a small amount of data, the effect is not obvious, because we have to spend time on data blocking.

The five factors that affect the performance of parallel flow are: data size, source data structure, whether the values are boxed, the number of available CPU cores, and the time spent processing each element.

When discussing the types of operating each block separately in the flow, it can be divided into two different operations: stateless and stateful. In the whole process of stateless operation, there is no need to maintain the state, and stateful operation has the overhead and limitation required to maintain the state. If we can avoid stateful operation and choose stateless operation, we can get better parallel performance. Stateless operations include map, filter and flatmap, and stateful operations include sorted, distinct and limit.

4、 Default method

The stream operation is added in java8. How can the custom collection mylist in the third-party class library be compatible? You can't upgrade java8, and all the collection implementations in the third-party class library can't be used, can you?

To this end, java8 introduces the concept of "default method" in the interface! The default method refers to the method defined in the interface that contains the method body. The method name is prefixed with the default keyword. The default method appears for backward compatibility with Java 8.

public interface Iterable<T> {
    /**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Unless otherwise specified by the implementing class,* actions are performed in the order of iteration (if an iteration order
     * is specified).  Exceptions thrown by the action are relayed to the
     * caller.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

Look at this iteratable in Java 8 The default method foreach (consumer action) in Java means "if you don't implement the foreach method, use mine".

In addition to adding a new keyword default, the default method is slightly different from the normal method in inheritance rules:

5、 Other

public static List<AssistantVO> getAssistant(Long tenantId) {
    // ofNullable 如果 value 为null,会构建一个空对象。
    Optional<List<AssistantVO>> assistantVO = Optional.ofNullable(ASSISTANT_MAP.get(tenantId));
    // orElse 如果 value 为null,选择默认对象。 
    assistantVO.orElse(ASSISTANT_MAP.get(DEFAULT_TENANT));
    return assistantVO.get();
}
Map<String,String> map = new HashMap<>(8);
map.put("java","java");
map.put("PHP","PHP");
String python = map.computeIfAbsent("python",k -> k.toUpperCase());
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>