Data structureLambda

?Author: Xiao Hu_Bu Muzhu
Author’s homepage: Xiao Hu_Bumuzzled’s personal homepage
Included column: A brief discussion of data structure
Continue to update articles and follow bloggers to avoid detours. Thank you for your support

Lambda expression

  • 1. Background
    • 1.1 Grammar
    • 1.2 Functional interface
  • 2. Basic use
  • 3. Variable capture
    • 3.1 Anonymous inner classes
    • 3.2 Variable capture of anonymous inner classes
    • 3.3 Lambda variable capture
  • 4. Use of Lambda in collections
    • 4.2 Collection interface
    • 4.2 List interface
    • 4.3 Map interface


1. Background

Lambda expressions allow you to replace functional interfaces with expressions. A lambda expression is just like a method in that it provides a normal parameter list and a body (which can be an expression or a code block) that uses these parameters. Lambda expression (Lambda expression), named after the lambda calculus in mathematics, can also be called closure (Closure).

1.1 Grammar

Basic syntax: (parameters) -> expression or (parameters) ->{ statements; }

  • paramaters: Similar to the formal parameter list in a method, the parameters here are parameters in the functional interface. The parameter types here can be explicitly declared, or they can be implicitly inferred by the JVM without being declared. In addition, the parentheses can be omitted when there is only one inferred type.
  • ->: Can be understood as meaning “to be used for”
  • Method body: It can be an expression or a code block, and is the implementation of the method in the functional interface. A code block can return a value or nothing. The code block here is equivalent to the method body of the method. If it is an expression, it can also return a value or nothing.
// 1. No parameters required, return value is 2
() -> 2
// 2. Receive a parameter (numeric type) and return 2 times its value
x -> 2*x
// 3. Accept 2 parameters (numbers) and return their sum
(x, y) -> x + y
// 4. Receive 2 int type integers and return their product
(int x, int y) -> x * y
// 5. Accept a string object and print it on the console without returning any value (it looks like it returns void)
(String s) -> System.out.print(s)

1.2 Functional interface

Functional interface definition: an interface has and only one abstract method

  1. If an interface has only one abstract method, then the interface is a functional interface
  2. If we declare the @FunctionalInterface annotation on an interface, the compiler will require the interface according to the definition of a functional interface. In this way, if there are two abstract methods, an error will be reported during program compilation. So, in a sense, as long as you ensure that there is only one abstract method in your interface, you don’t need to add this annotation. It will be automatically detected after adding it.

Definition:

@FunctionalInterface
interface NoParameterNoReturn {<!-- -->
//Note: There can only be one method
void test();
}
@FunctionalInterface
interface NoParameterNoReturn {<!-- -->
void test();
default void test2() {<!-- -->
System.out.println("JDK1.8 new feature, default method can have specific implementation");
}
}

2. Basic usage

Lambda can be understood as: Lambda is a simplification of anonymous inner classes. It actually creates a class, implements the interface, and overrides the interface methods.

First implement a few interfaces:

//No return value and no parameters
@FunctionalInterface
interface NoParameterNoReturn {<!-- -->
void test();
}
//No return value, one parameter
@FunctionalInterface
interface OneParameterNoReturn {<!-- -->
void test(int a);
}
//Multiple parameters without return value
@FunctionalInterface
interface MoreParameterNoReturn {<!-- -->
void test(int a,int b);
}
//With return value and no parameters
@FunctionalInterface
interface NoParameterReturn {<!-- -->
int test();
}
//There is one parameter with return value
@FunctionalInterface
interface OneParameterReturn {<!-- -->
int test(int a);
}
// Multiple parameters with return value
@FunctionalInterface
interface MoreParameterReturn {<!-- -->
int test(int a,int b);
}

Specific usage examples of Lambda:

public class TestDemo1 {<!-- -->
    public static void main(String[] args) {<!-- -->
        NoParameterNoReturn noParameterNoReturn = ()->{<!-- -->
            System.out.println("No parameters, no return value");
        };
        noParameterNoReturn.test();

        OneParameterNoReturn oneParameterNoReturn = (int a)->{<!-- -->
            System.out.println("One parameter has no return value:" + a);
        };
        oneParameterNoReturn.test(10);

        MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{<!-- -->
            System.out.println("Multiple parameters have no return value:" + a + " " + b);
        };
        moreParameterNoReturn.test(20,30);

        NoParameterReturn noParameterReturn = ()->{<!-- -->
            System.out.println("There is a return value and no parameters!");
            return 40;
        };
//Receive the return value of the function
        int ret = noParameterReturn.test();
        System.out.println(ret);
        OneParameterReturn oneParameterReturn = (int a)->{<!-- -->
            System.out.println("There is a return value and a parameter!");
            return a;
        };
        ret = oneParameterReturn.test(50);
        System.out.println(ret);

        MoreParameterReturn moreParameterReturn = (int a,int b)->{<!-- -->
            System.out.println("There are multiple parameters with return value!");
            return a + b;
        };
        ret = moreParameterReturn.test(60,70);
        System.out.println(ret);
    }
}

3. Variable capture

3.1 Anonymous inner classes

Anonymous inner classes are inner classes without names. In Java’s anonymous classes, there will be variable capture.

Anonymous inner classes cannot define any static members or methods.
Methods in anonymous inner classes cannot be abstract.
Anonymous inner classes must implement all abstract methods of the interface or abstract parent class.
External class member variables or member methods accessed by anonymous inner classes must be decorated with static.

Examples of usage of anonymous inner classes:

class Test{<!-- -->
public void func(){<!-- -->
System.out.println("func()");
}
}

public class TestDemo {<!-- -->
public static void main(String[] args) {<!-- -->
new Test(){<!-- -->
@Override
public void func() {<!-- -->
System.out.println("I am an internal class and have overridden the func method!");
}
};
}
}

3.2 Variable capture of anonymous inner classes

class Test {<!-- -->
public void func(){<!-- -->
System.out.println("func()");
}
}

public class TestDemo {<!-- -->
public static void main(String[] args) {<!-- -->
int a = 100;
new Test(){<!-- -->
@Override
public void func() {<!-- -->
System.out.println("I am an internal class and have overridden the func method!");
System.out.println("I captured the variable a == " + a
 + "I am a constant, or a variable whose value has not changed! ");
}
};
}
}

The variable a in the above code is the captured variable. This variable is either final-modified. If it is not final-modified, you must ensure that it has not been modified before use.

The following code is the wrong code:

public class TestDemo {<!-- -->
public static void main(String[] args) {<!-- -->
int a = 100;
new Test(){<!-- -->
@Override
public void func() {<!-- -->
a = 99;
System.out.println("I am an internal class and have overridden the func method!");
System.out.println("I captured the variable a == " + a
 + "I am a constant, or a variable whose value has not changed!");
}
};
}
}
//This code will compile directly and report an error

3.3 Lambda variable capture

@FunctionalInterface
interface NoParameterNoReturn {<!-- -->
void test();
}

public static void main(String[] args) {<!-- -->
int a = 10;
NoParameterNoReturn noParameterNoReturn = ()->{<!-- -->
// a = 99; error
System.out.println("Capture variable:" + a);//Modification is not allowed either
};
noParameterNoReturn.test();
}

4. The use of Lambda in collections

In order to make Lambda and Java’s collection classes better work together, some new interfaces have also been added to the collection to connect with Lambda expressions.

Corresponding interface New method
Collection removeIf() spliterator() stream() parallelStream() forEach()
List replaceAll() sort()
Map getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent()computeIfPresent() compute() merge()

4.2 Collection interface

forEach() method demonstration
This method is in the interface Iterable, and its prototype is as follows:

default void forEach(Consumer<? super T> action) {<!-- -->
Objects.requireNonNull(action);
for (T t : this) {<!-- -->
action.accept(t);
}
}

This method means: perform the action specified by action on each element in the container.
Example:

public static void main(String[] args) {<!-- -->
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("world");
        list.add("hello");
        list.add("lambda");
        list.forEach(new Consumer<String>(){<!-- -->
            @Override
            public void accept(String str){<!-- -->
            //Simply traverse the elements in the collection.
                System.out.print(str + " ");//Hello world hello lambda
            }
        });
    }

Modify it into a Lambda expression as follows:

 public static void main(String[] args) {<!-- -->
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("world");
        list.add("hello");
        list.add("lambda");
//Indicates calling a method without parameters, which executes the statement within the curly braces, which is the original function body content.
        list.forEach(s -> {<!-- -->
            System.out.println(s);//Hello world hello lambda
        });
    }

4.2 List interface

Demonstration of sort() method
Sort method source code: This method sorts container elements according to the comparison rules specified by c.

public void sort(Comparator<? super E> c) {<!-- -->
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {<!-- -->
throw new ConcurrentModificationException();
}
modCount + + ;
}

Usage example:

public static void main(String[] args) {<!-- -->
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("world");
        list.add("hello");
        list.add("lambda");
        list.sort(new Comparator<String>() {<!-- -->
            @Override
            public int compare(String str1, String str2){<!-- -->
//Compare the length here
                return str1.length()-str2.length();
            }
        });
        System.out.println(list);//[Hello, world,hello,lambda]
    }

Modify it into a Lambda expression as follows:

public static void main(String[] args) {<!-- -->
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("world");
        list.add("hello");
        list.add("lambda");
        //Call the method with 2 parameters and return the difference in length
        list.sort((str1,str2)-> str1.length()-str2.length());
        System.out.println(list);//[Hello, world,hello,lambda]
    }

4.3 Map interface

HashMap’s forEach()
The method prototype is as follows:

default void forEach(BiConsumer<? super K, ? super V> action) {<!-- -->
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {<!-- -->
K k;
V v;
try {<!-- -->
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {<!-- -->
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}

The function is to perform the operation specified by action on each mapping in the Map.
Code example:

 public static void main(String[] args) {<!-- -->
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "world");
        map.put(3, "hello");
        map.put(4, "lambda");
        map.forEach(new BiConsumer<Integer, String>(){<!-- -->
            @Override
            public void accept(Integer k, String v){<!-- -->
                System.out.println(k + "=" + v);
            }
        });
    }
//1=hello
//2=world
//3=hello
//4=lambda

Modify to Lambda expression:

public static void main(String[] args) {<!-- -->
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");
        map.forEach((k,v)-> System.out.println(k + "=" + v));
    }