• 周六. 10 月 5th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Common skills of using java lamda expression in detail

King Wang

1 月 3, 2022

Java8 Add some useful methods to the container , Some of these methods are to improve the original function , Some are for the introduction of functional programming (Lambda expression ), Learning and using these methods will help us write more concise and effective code . This article uses ArrayList and HashMap For example , Explain Java8 Collections framework (Java Collections Framework) The use of the new addition method in .

Define the object :

public class Apple {
private Integer id;
private String name;
private BigDecimal money;
private Integer num;
public Apple(Integer id, String name, BigDecimal money, Integer num) {
this.id = id;
this.name = name;
this.money = money;
this.num = num;
}
}
Test data
List<Apple> appleList = new ArrayList<>();// Deposit apple A collection of objects
Apple apple1 = new Apple(1," Apple 1",new BigDecimal("3.25"),10);
Apple apple12 = new Apple(1," Apple 2",new BigDecimal("1.35"),20);
Apple apple2 = new Apple(2," Banana ",new BigDecimal("2.89"),30);
Apple apple3 = new Apple(3," litchi ",new BigDecimal("9.99"),40);
appleList.add(apple1);
appleList.add(apple12);
appleList.add(apple2);
appleList.add(apple3);

 1、 grouping

List The object element inside , To group by an attribute , for example , With id grouping , take id Put the same together :

//List With ID grouping Map<Integer,List<Apple>>


Map<Integer, List<Apple>> groupBy = appleList.stream().collect(Collectors.groupingBy(Apple::getId));
System.err.println("groupBy:"+groupBy);
{1=[Apple{id=1, name=' Apple 1', money=3.25, num=10}, Apple{id=1, name=' Apple 2', money=1.35, num=20}], 2=[Apple{id=2, name=' Banana ', money=2.89, num=30}], 3=[Apple{id=3, name=' litchi ', money=9.99, num=40}]}

 2、List turn Map

id by key,apple The object is value

 

/**
* List -> Map
* It should be noted that :
* toMap If the collection object has duplicate key, Will report a mistake Duplicate key ....
* apple1,apple12 Of id All for 1.
* It can be used (k1,k2)->k1 To set up , If there are duplicates key, The retention key1, Abandon key2
*/
Map<Integer, Apple> appleMap = appleList.stream().collect(Collectors.toMap(Apple::getId, a -> a,(k1,k2)->k1));

 

 Print :
{1=Apple{id=1, name=' Apple 1', money=3.25, num=10}, 2=Apple{id=2, name=' Banana ', money=2.89, num=30}, 3=Apple{id=3, name=' litchi ', money=9.99, num=40}}

List turn Map Press key duplicate removal   repeat key Value processing

Map<String, ObjectBasicBO> result =
Optional.ofNullable(list).get().stream().collect(
Collectors.toMap(ObjectBasicBO::getValue, Function.identity(), (old, key2) -> key2));

 List duplicate removal

 data :
BookBo bookBo1 = new BookBo("1", " Chinese language and literature ");
BookBo bookBo2 = new BookBo("2", " mathematics ");
BookBo bookBo3 = new BookBo("3", " English ");
BookBo bookBo4 = new BookBo("4", " Geography ");
BookBo bookBo5 = new BookBo("5", " biological ");
BookBo bookBo6 = new BookBo("1", " Chinese language and literature ");
List<BookBo> bookBoList = Arrays.asList(bookBo1, bookBo2, bookBo3, bookBo4, bookBo5, bookBo6);
 List<BookBo> distinctList = bookBoList
//collectingAndThen Collect and then in
.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()->new TreeSet<>(Comparator.comparing(o-> o.getName()))), ArrayList::new))
.stream().sorted(Comparator.comparing(BookBo::getName)).collect(Collectors.toList());

 3、 Filter Filter

Filter out the qualified elements from the collection :

// Filter out qualified data
List<Apple> filterList = appleList.stream().filter(a -> a.getName().equals(" Banana ")).collect(Collectors.toList());
System.err.println("filterList:"+filterList);
[Apple{id=2, name=' Banana ', money=2.89, num=30}]

 
4. Sum up

Sum the data in the set according to a certain attribute :

// Calculation Total sum
BigDecimal totalMoney = appleList.stream().map(Apple::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
System.err.println("totalMoney:"+totalMoney); //totalMoney:17.48

 

5. Find the largest… In the stream minimum value

Collectors.maxBy and Collectors.minBy To calculate the maximum or minimum value in the flow .

Optional<Dish> maxDish = Dish.menu.stream().
collect(Collectors.maxBy(Comparator.comparing(Dish::getCalories)));
maxDish.ifPresent(System.out::println);
Optional<Dish> minDish = Dish.menu.stream().
collect(Collectors.minBy(Comparator.comparing(Dish::getCalories)));
minDish.ifPresent(System.out::println);

 6. duplicate removal

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;
// according to id duplicate removal
List<Person> unique = appleList.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(comparingLong(Apple::getId))), ArrayList::new)
);

  Multi field splicing and de duplication

List<ClassEntity> distinctClass = classEntities.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getProfessionId() + ";" + o.getGrade()))), ArrayList::new));

=============================== The concept is as follows ========================================== 

Preface

Let’s start with the most familiar Java Collections framework (Java Collections Framework, JCF) Start talking about .

For introduction Lambda expression ,Java8 Added java.util.funcion package , It contains commonly used The function interface , This is a Lambda The basis of expression ,Java The collection framework also adds some interfaces , So as to meet with Lambda Expression docking .

First, let’s review Java The interface inheritance structure of the collection framework :

JCF_Collection_Interfaces

The interface class marked in green in the figure above , It means that Java8 A new interface method has been added , Of course, because of inheritance , Their corresponding subclasses will also inherit these new methods . The following table details these methods .

The interface name Java8 New ways to join
Collection removeIf() spliterator() stream() parallelStream() forEach()
List replaceAll() sort()
Map getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()

Most of these new methods will use java.util.function The interface under the package , This means that most of these methods follow Lambda Expression related . We will learn these methods one by one .

Collection The new method in

As shown above , Interface Collection and List Some new methods have been added , We are List Subclasses of ArrayList For example . understand Java7ArrayList Realization principle , It will help to understand .

forEach()

The method is signed as void forEach(Consumer<? super E> action), The purpose is to execute… On each element in the container action The designated action , among Consumer It’s a function interface , There is only one method to be implemented void accept(T t)( We’ll see that later , It doesn’t matter what this method is called , You don’t even need to remember its name ).

demand : Suppose you have a list of strings , You need to print out all of them that are longer than 3 String .

Java7 And before we could use enhanced for Cycle to achieve :

// Use Zeng Qiang for Loop iteration
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
for(String str : list){
if(str.length()>3)
System.out.println(str);
}

Now use forEach() Method combined with anonymous inner class , It can be realized in this way :

// Use forEach() Combined with anonymous inner class iteration
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach(new Consumer<String>(){
@Override
public void accept(String str){
if(str.length()>3)
System.out.println(str);
}
});

The above code calls forEach() Method , And use anonymous inner class to implement Comsumer Interface . So far we haven’t seen the benefits of this design , But don’t forget Lambda expression , Use Lambda The expression is implemented as follows :

// Use forEach() combination Lambda Expression iteration
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach( str -> {
if(str.length()>3)
System.out.println(str);
});

The above code is for forEach() Method passes in a Lambda expression , We don’t need to know accept() Method , I don’t need to know Consumer Interface , Type derivation helps us do everything .

removeIf()

The signature of this method is boolean removeIf(Predicate<? super E> filter), Role is Delete all satisfied… In the container filter The element that specifies the condition , among Predicate It’s a function interface , There is only one method to be implemented boolean test(T t), The name of the same method doesn’t matter at all , Because you don’t need to write the name .

demand : Suppose you have a list of strings , You need to delete all of them that are longer than 3 String .

We know that if we need to delete the container in the iterative process, we must use the iterator , Otherwise it will throw ConcurrentModificationException, So the traditional way to write these tasks is :

// Use iterators to delete list elements
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Iterator<String> it = list.iterator();
while(it.hasNext()){
if(it.next().length()>3) // Delete length greater than 3 The elements of
it.remove();
}

Now use removeIf() Method combined with anonymous inner class , This is how we achieve :

// Use removeIf() Combined with anonymous name inner class implementation
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(new Predicate<String>(){ // Delete length greater than 3 The elements of
@Override
public boolean test(String str){
return str.length()>3;
}
});

The above code uses removeIf() Method , And use anonymous inner class to implement Precicate Interface . I believe you have thought of using Lambda How should expressions be written :

// Use removeIf() combination Lambda Expression implementation
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(str -> str.length()>3); // Delete length greater than 3 The elements of 

Use Lambda Expressions don’t need to be memorized Predicate The interface name , There is no need to remember test() Method name , Just know that there needs to be a return Boolean type Lambda Just expressions .

replaceAll()

The signature of this method is void replaceAll(UnaryOperator<E> operator), Role is Execution of each element operator Specified operation , And replace the original element with the result of the operation . among UnaryOperator It’s a function interface , There is only one function to be implemented T apply(T t).

demand : Suppose you have a list of strings , Make all of them longer than 3 The elements of are converted to uppercase , The rest of the elements remain the same .

Java7 Before that, there seems to be no elegant way :

// Use subscripts to implement element substitution
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
for(int i=0; i<list.size(); i++){
String str = list.get(i);
if(str.length()>3)
list.set(i, str.toUpperCase());
}

Use replaceAll() Methods combined with anonymous inner classes can be implemented as follows :

// Use anonymous inner class to implement
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(new UnaryOperator<String>(){
@Override
public String apply(String str){
if(str.length()>3)
return str.toUpperCase();
return str;
}
});

The above code calls replaceAll() Method , And use anonymous inner class to implement UnaryOperator Interface . We know that we can use a simpler Lambda Expression implementation :

// Use Lambda Expression implementation
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(str -> {
if(str.length()>3)
return str.toUpperCase();
return str;
});

sort()

The method is defined in List Interface , Method signature is void sort(Comparator<? super E> c), The method according to c The specified comparison rules sort the container elements .Comparator We are not new to interface , There’s a way int compare(T o1, T o2) Need to achieve , Obviously this interface is a function interface .

demand : Suppose you have a list of strings , Sort elements by increasing string length .

because Java7 And before sort() Method in Collections In the tool class , So the code should be written like this :

// Collections.sort() Method
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Collections.sort(list, new Comparator<String>(){
@Override
public int compare(String str1, String str2){
return str1.length()-str2.length();
}
});

Now it can be used directly List.sort() Method , combination Lambda expression , It can be written like this :

// List.sort() Method combination Lambda expression
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.sort((str1, str2) -> str1.length()-str2.length());

spliterator()

Method signature is Spliterator<E> spliterator(), This method returns the… Of the container Separable iterators . From the point of view of name, this method follows iterator() It’s a little like that , We know Iterator It’s used to iterate over containers ,Spliterator It has a similar effect , But they are different :

  1. Spliterator It can be like Iterator So iterate one by one , You can also iterate in batches . Batch iteration can reduce the cost of iteration .
  2. Spliterator It’s separable , One Spliterator You can call Spliterator<T> trySplit() Try to divide it into two . One is this, The other is the newly returned one , The two iterators represent elements that do not overlap .

It can be done by ( many times ) call Spliterator.trySplit() Method to break down the load , For multithreading .

stream() and parallelStream()

stream() and parallelStream() , respectively, Return… Of the container Stream View representation , The difference is parallelStream() Return to parallel Stream.Stream yes Java The core class of functional programming , We’ll learn about .

Map The new method in

comparison Collection,Map There are more ways , We use HashMap For example, to explore one by one . understand Java7HashMap Realization principle , It will help to understand .

forEach()

The signature of this method is void forEach(BiConsumer<? super K,? super V> action), Role is Yes Map Execution of each map in action Specified operation , among BiConsumer It’s a function interface , There’s a way to do it void accept(T t, U u).BinConsumer Interface name and accept() Method names don’t matter , Please don’t remember them .

demand : Let’s say that there is a number to Map, Please export Map All mapping relationships in .

Java7 And the previous classic code is as follows :

// Java7 And previous iterations Map
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for(Map.Entry<Integer, String> entry : map.entrySet()){
System.out.println(entry.getKey() + "=" + entry.getValue());
}

Use Map.forEach() Method , Combine anonymous inner classes , The code is as follows :

// Use forEach() Combined with anonymous inner class iteration Map
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach(new BiConsumer<Integer, String>(){
@Override
public void accept(Integer k, String v){
System.out.println(k + "=" + v);
}
});

The above code calls forEach() Method , And use anonymous inner class to implement BiConsumer Interface . Of course , In the actual scenario, no one uses anonymous inner class writing , Because there is Lambda expression :

// Use forEach() combination Lambda Expression iteration Map
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((k, v) -> System.out.println(k + "=" + v));
}

getOrDefault()

This method follows Lambda Expressions don’t matter , But it’s useful . Method signature is V getOrDefault(Object key, V defaultValue), Role is According to the given key Inquire about Map Corresponding value, Return if not found defaultValue. Using this method, the programmer can save the trouble of querying whether the specified key value exists .

demand ; Let’s say that there is a number to Map, Output 4 Corresponding English words , If not, output NoValue

// Inquire about Map The value specified in , Use the default when it doesn't exist
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
// Java7 And what we did before
if(map.containsKey(4)){ // 1
System.out.println(map.get(4));
}else{
System.out.println("NoValue");
}
// Java8 Use Map.getOrDefault()
System.out.println(map.getOrDefault(4, "NoValue")); // 2

putIfAbsent()

This method follows Lambda Expressions don’t matter , But it’s useful . Method signature is V putIfAbsent(K key, V value), The function is only in non-existent key The mapping or mapping value of a value is null when , Only then value The specified value is put into Map in , Otherwise it’s not right Map Make changes . This method combines condition judgment with assignment , More convenient to use .

remove()

We all know Map There is one of them. remove(Object key) Method , According to the designation key Value delete Map Mapping relationship in ;Java8 Added remove(Object key, Object value) Method , Only in the present Map in key It just maps to value when Delete the map , Otherwise, do nothing .

replace()

stay Java7 And before , If you want to replace Map The mapping relationship in can be obtained by put(K key, V value) Method realization , This method always replaces the original value with a new value . In order to control the replacement behavior more precisely ,Java8 stay Map Two of them replace() Method , They are as follows :

  • replace(K key, V value), Only in the present Map in key When the mapping of exists Only use value To replace the original value , Otherwise, do nothing .
  • replace(K key, V oldValue, V newValue), Only in the present Map in key The mapping of exists and is equal to oldValue when Only use newValue To replace the original value , Otherwise, do nothing .

replaceAll()

The signature of this method is replaceAll(BiFunction<? super K,? super V,? extends V> function), The effect is right. Map Execution of each map in function Specified operation , And use function Replace the original value, among BiFunction It’s a function interface , There’s a way to do it R apply(T t, U u). Don’t be intimidated by so many function interfaces , Because we don’t need to know their names when we use them .

demand : Let’s say that there is a number to Map, Please convert the words in the original mapping relationship to uppercase .

Java7 And the previous classic code is as follows :

// Java7 And replace all of Map All mapping relationships in
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for(Map.Entry<Integer, String> entry : map.entrySet()){
entry.setValue(entry.getValue().toUpperCase());
}

Use replaceAll() Method combined with anonymous inner class , The implementation is as follows :

// Use replaceAll() Combined with anonymous inner class
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.replaceAll(new BiFunction<Integer, String, String>(){
@Override
public String apply(Integer k, String v){
return v.toUpperCase();
}
});

The above code calls replaceAll() Method , And use anonymous inner class to implement BiFunction Interface . further , Use Lambda The expression is implemented as follows :

// Use replaceAll() combination Lambda Expression implementation
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.replaceAll((k, v) -> v.toUpperCase());

It’s incredibly simple .

merge()

The signature of this method is merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction), Role is :

  1. If Map in key The mapping does not exist or is null, Will value( It can’t be null) Related to key On ;
  2. Otherwise execution remappingFunction, If the execution result is not null Then use the result with key relation , Otherwise, in the Map Delete in key Mapping .

Parameters in BiFunction The function interface has been introduced before , There’s a way to do it R apply(T t, U u)

merge() Although the semantics of methods are somewhat complicated , But the way it’s used is clear , A common scenario is to splice new error information onto the original information , such as :

map.merge(key, newMsg, (v1, v2) -> v1+v2);

compute()

The signature of this method is compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction), The effect is to turn remappingFunction The results of the calculation are related to key On , If the result is null, It’s in Map Delete in key Mapping .

To achieve the above merge() An example of error information splicing in the method , Use compute() The code is as follows :

map.compute(key, (k,v) -> v==null ? newMsg : v.concat(newMsg));

computeIfAbsent()

The signature of this method is V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction), Role is : Only in the present Map in non-existent key The mapping or mapping value of a value is null when , Only called mappingFunction, And in mappingFunction The execution result is not null when , Follow the result with key relation .

Function It’s a function interface , There’s a way to do it R apply(T t)

computeIfAbsent() Often used to Map One of the key Value to create an initialization map . For example, we need to implement a multivalued mapping ,Map The definition of may be Map<K,Set<V>>, Want to Map Put the new value , It can be realized by the following code :

Map<Integer, Set<String>> map = new HashMap<>();
// Java7 And previous implementations
if(map.containsKey(1)){
map.get(1).add("one");
}else{
Set<String> valueSet = new HashSet<String>();
valueSet.add("one");
map.put(1, valueSet);
}
// Java8 How to implement
map.computeIfAbsent(1, v -> new HashSet<String>()).add("yi");

Use computeIfAbsent() Combine condition judgment with addition operation , Make the code more concise .

computeIfPresent()

The signature of this method is V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction), Action follows computeIfAbsent() contrary , namely , Only in the present Map in There is key The mapping of values is not null when , Only called remappingFunction, If remappingFunction The execution result is null, Delete key Mapping , Otherwise use this result to replace key The original mapping .

The function of this function is equivalent to the following code :

// Java7 And before computeIfPresent() Equivalent code
if (map.get(key) != null) {
V oldValue = map.get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null)
map.put(key, newValue);
else
map.remove(key);
return newValue;
}
return null;

summary

  1. Java8 Add some useful methods to the container , Some of these methods are for Perfect the original function , Some are for Introduce functional programming , Learning and using these methods will help us write more concise and effective code .
  2. The function interface Though many , But most of the time we don’t need to know their names at all , Writing Lambda Expression time type inference helps us do everything .

this paper github Address

https://github.com/CarpenterLee

https://github.com/CarpenterLee/JavaLambdaInternals

Reference resources :

https://blog.csdn.net/lu930124/article/details/77595585

发表回复