当前位置:网站首页>The solution to the complexity brought by lambda expression
The solution to the complexity brought by lambda expression
2022-07-02 04:17:00 【Mingmingruyue senior】
One 、 background
Java 8 Of Lambda The expression is no longer “ New characteristics ”.
Many people once resisted Lambda expression , Now it's almost standard .
The most common in actual development is , Many people use Stream To handle collection classes .
But because of Lambda Expression abuse , The code becomes less readable , So how to solve ?
This article will discuss this problem , And give some solutions .
Two 、 view
about Lambda Expression or Stream They have different views .
2.1 Support
(1) Use Lambda Expressions can reduce the creation of classes or methods , It's simple to write .
Such as :
import java.util.HashMap;
import java.util.Map;
public class LambdaMapDemo {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
map.put(i, String.valueOf(i));
}
// It was written in the original way
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println("k:" + entry.getKey() + " -> v:" + entry.getValue());
}
}
Use Lambda Writing
map.forEach((k, v) -> {
System.out.println("k:" + k + " -> v:" + v);
});
}
(2) Use Stream You can enjoy chain programming .
(3) Some people see others using , There seems to be some high-end , Or worry about being eliminated and use it a lot .
2.2 against
Some people are right lambda The expression disagrees .
(1) They think a lot of lambda The expression is written The code is not easy to understand .
(2) There are many old people in the team , It's not easy to accept new things , The use of Lambda expression .

Such as :Stream The widespread use of has brought many template methods .
List<String> tom = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> dog.getName().toLowerCase()).collect(Collectors.toList());
Even often someone will be in Stream Of map Write a lot of conversion code in functions .
import lombok.Data;
@Data
public class DogDO {
private String name;
private String nickname;
private String address;
private String owner;
}
DogVO and DogDO Structure is the same .
List<DogVO> result = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> {
DogVO dogVO = new DogVO();
dogVO.setName(dog.getName());
dogVO.setAddress(dog.getAddress());
dogVO.setOwner(dog.getOwner());
return dogVO;
}).collect(Collectors.toList());
What's more, the whole Stream The expression result is passed into the method as a parameter :
result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> {
DogVO dogVO = new DogVO();
dogVO.setName(dog.getName());
dogVO.setAddress(dog.getAddress());
dogVO.setOwner(dog.getOwner());
return dogVO;
}).collect(Collectors.toList()));
When a large number of the above phenomena occur in a method , The code can't be read .
(3) Some people are worried about Stream It will bring some side effects .
2.3 My opinion
Lambda It's a double-edged sword , When using, you need to grasp the degree .
How to decode Lambda The complexity of expressions , Look at part four .
3、 ... and 、 Underlying principle
See my other article
《 In depth understanding of Lambda expression 》
Four 、 Suggest
Lambda It simplifies the code , But grasp the degree , If abused lambda expression , Code readability can be poor .
4.1 Use method reference
List<String> names = new LinkedList<>();
names.addAll(users.stream().map(user -> user.getName()).filter(userName -> userName != null).collect(Collectors.toList()));
names.addAll(users.stream().map(user -> user.getNickname()).filter(nickname -> nickname != null).collect(Collectors.toList()));
It can be optimized to :
List<String> names = new LinkedList<>();
names.addAll(users.stream().map(User::getName).filter(Objects::nonNull).collect(Collectors.toList()));
names.addAll(users.stream().map(User::getNickname).filter(Objects::nonNull).collect(Collectors.toList()));
4.2 Extract complex code
For some complex logic 、 For some logic that needs to be reused , It is recommended to encapsulate into a separate class .
Such as Stream Commonly used in parameters java.util.function Under bag Predicate 、Function 、Consumer Classes and Comparator etc. .
result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> {
DogVO dogVO = new DogVO();
dogVO.setName(dog.getName());
dogVO.setAddress(dog.getAddress());
dogVO.setOwner(dog.getOwner());
return dogVO;
}).collect(Collectors.toList()));
The transformation is as follows :
import java.util.function.Function;
public class DogDO2VOConverter implements Function<DogDO, DogVO> {
@Override
public DogVO apply(DogDO dogDO) {
DogVO dogVO = new DogVO();
dogVO.setName(dogDO.getName());
dogVO.setAddress(dogDO.getAddress());
dogVO.setOwner(dogDO.getOwner());
return dogVO;
}
}
reform
result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(new DogDO2VOConverter()).collect(Collectors.toList()));
Or define static methods
public class DogDO2VOConverter {
public static DogVO toVo(DogDO dogDO) {
DogVO dogVO = new DogVO();
dogVO.setName(dogDO.getName());
dogVO.setAddress(dogDO.getAddress());
dogVO.setOwner(dogDO.getOwner());
return dogVO;
}
}
Just use method calls directly
result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()));
4.3 Don't put stream Put the operation in the method parameter
Just like the code above
result.addAll(dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()));
Many people write code , Like to be Stream Operations are placed in method parameters to save a local variable .
Personally, I am very opposed to this kind of behavior , This greatly reduces the readability of the code .
We should have Stream The operation of defines a return value with clear meaning , And then use .
Such as :
List<DogVO> toms = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList());
result.addAll(toms);
4.4 Lambda The expression should not be too long
Many people taste the sweetness of chain programming , Always like to write long code .
Such as :
Optional.ofNullable(dogs).orElse(new ArrayList<>()).stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()));
But it's hard to look at this code , When a lot of this code appears in a function , I'm spitting blood .
For this long Lambda Expression writing , It is recommended to split it as much as possible .
List<Dog> dogs = Optional.ofNullable(dogs).orElse(new ArrayList<>());
List<String> toms = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(DogDO2VOConverter::toVo).collect(Collectors.toList()))
And then further dogs.stream This part of the logic is encapsulated into sub functions .
List<String> toms = getDogNamesStartWithTom(dogs)
4.5 Template methods use generic encapsulation
If you find a lot of use in your project Lambda, And the logic of many codes is very similar , Consider using generic wrapper classes to simplify your code .
Here are two simple examples .
4.5.1 Stream Object conversion
In the actual development, there are a lot of code like filtering before conversion :
List<DogVO> vos = dogs.stream().map(DogDO2VOConverter::toVo).collect(Collectors.toList())
In fact, this kind of writing is used to , But if a method appears many times, it is very unreadable .
After being encapsulated into a tool class, it is more concise :
List<DogVO> vos = MyCollectionUtils.convert(dogs,DogDO2VOConverter::toVo);
Tool class :
public class MyCollectionUtils {
public static <S, T> List<T> convert(List<S> source, Function<S, T> function) {
if (CollectionUtils.isEmpty(source)) {
return new ArrayList<>();
}
return source.stream().map(function).collect(Collectors.toList());
}
public static <S, T> List<T> convert(List<S> source, Predicate<S> predicate, Function<S, T> function) {
if (CollectionUtils.isEmpty(source)) {
return new ArrayList<>();
}
return source.stream().filter(predicate).map(function).collect(Collectors.toList());
}
}
By encapsulating common template methods into tool classes , It can greatly simplify the code when using .
4.5.2 Spring Strategic model cases
Such as 《 Skillfully use Spring Automatic injection implementation policy mode upgrade 》 I mentioned , The following cases :
Defining interfaces
public interface Handler {
String getType();
void someThing();
}
VIP User implementation :
import org.springframework.stereotype.Component;
@Component
public class VipHandler implements Handler{
@Override
public String getType() {
return "Vip";
}
@Override
public void someThing() {
System.out.println("Vip user , Take the logic here ");
}
}
Ordinary users realize :
@Component
public class CommonHandler implements Handler{
@Override
public String getType() {
return "Common";
}
@Override
public void someThing() {
System.out.println(" Ordinary users , Take the logic here ");
}
}
simulation Service Use in :
@Service
public class DemoService implements ApplicationContextAware {
private Map<String, List<Handler>> type2HandlersMap;
public void test(){
String type ="Vip";
for(Handler handler : type2HandlersMap.get(type)){
handler.someThing();;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Handler> beansOfType = applicationContext.getBeansOfType(Handler.class);
beansOfType.forEach((k,v)->{
type2HandlersMap = new HashMap<>();
String type =v.getType();
type2HandlersMap.putIfAbsent(type,new ArrayList<>());
type2HandlersMap.get(type).add(v);
});
}
}
among setApplicationContext The code inside is very similar .
You can write tool classes
import org.apache.commons.collections4.MapUtils;
import org.springframework.context.ApplicationContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class BeanStrategyUtils {
// structure type To more than one bean Mapping
public static <K,B> Map<K, List<B>> buildTypeBeansMap(ApplicationContext applicationContext, Class<B> beanClass, Function<B,K> keyFunc) {
Map<K, List<B>> result = new HashMap<>();
Map<String, B> beansOfType = applicationContext.getBeansOfType(beanClass);
if(MapUtils.isEmpty(beansOfType)){
return result;
}
for(B bean : beansOfType.values()){
K type = keyFunc.apply(bean);
result.putIfAbsent(type,new ArrayList<>());
result.get(type).add(bean);
}
return result;
}
// structure type To single bean Mapping
public static <K,B> Map<K, B> buildType2BeanMap(ApplicationContext applicationContext, Class<B> beanClass, Function<B,K> keyFunc) {
Map<K, B> result = new HashMap<>();
Map<String, B> beansOfType = applicationContext.getBeansOfType(beanClass);
if(MapUtils.isEmpty(beansOfType)){
return result;
}
for(B bean : beansOfType.values()){
K type = keyFunc.apply(bean);
result.put(type,bean);
}
return result;
}
}
After transformation
@Service
public class DemoService implements ApplicationContextAware {
private Map<String, List<Handler>> type2HandlersMap;
public void test(){
String type ="Vip";
for(Handler handler : type2HandlersMap.get(type)){
handler.someThing();;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
type2HandlersMap = BeanStrategyUtils.buildTypeBeansMap(applicationContext,Handler.class, Handler::getType);
}
}
A lot of people would say , Writing tools is also time-consuming .
however , After writing the tool method , Code repetition rate is reduced ; The code is more concise , Readability improvement ; Subsequent similar logic can realize code reuse , Development efficiency has also improved ; Fully staffed .
4.6 Use the reinforcement package
We talked about , You can reduce... By encapsulating tool classes Lambda The complexity of the code .
Besides , We can also consider using some enhancement packages to solve this problem .
4.6.1 StreamEx
Such as StreamEx
Maven rely on
https://mvnrepository.com/artifact/one.util/streamex
<!-- https://mvnrepository.com/artifact/one.util/streamex -->
<dependency>
<groupId>one.util</groupId>
<artifactId>streamex</artifactId>
<version>0.8.0</version>
</dependency>
Java 8 How to write it
Map<Role, List<User>> role2users = users.stream().collect(Collectors.groupingBy(User::getRole));
StreamEx How to write it :
Map<Role, List<User>> role2users = StreamEx.of(users).groupingBy(User::getRole);
The previous case
List<DogVO> vos = dogs.stream().map(DogDO2VOConverter::toVo).collect(Collectors.toList())
It can be changed to
List<DogVO> vos = StreamEx.of(dogs).map(DogDO2VOConverter::toVo).toList();
4.6.2 vavr
User documentation :https://docs.vavr.io/
Maven rely on
https://mvnrepository.com/artifact/io.vavr/vavr
<!-- https://mvnrepository.com/artifact/io.vavr/vavr -->
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>1.0.0-alpha-4</version>
</dependency>
Java 8 Writing in Chinese :
// = ["1", "2", "3"] in Java 8
Arrays.asList(1, 2, 3)
.stream()
.map(Object::toString)
.collect(Collectors.toList())
vavr Writing in Chinese :
// = Stream("1", "2", "3") in Vavr
Stream.of(1, 2, 3).map(Object::toString)
4.7 Add notes to each step
If you use chain programming , If there are many steps , It is recommended to add notes to each step , It's easier to understand .
4.8 Some scenes don't have to Lambda expression
If you find that... Is used in a function Lambda Too much time ( In practice , You'll find that more than half of a function is lambda expression , A great headache ), You can consider some things that are not easy to understand Lambda Change the writing to normal , Usually readability will be greatly improved .
List<String> names = new LinkedList<>();
names.addAll(users.stream().map(User::getName).filter(Objects::nonNull).collect(Collectors.toList()));
names.addAll(users.stream().map(User::getNickname).filter(Objects::nonNull).collect(Collectors.toList()));
Optimize to
List<String> names = new LinkedList<>();
for(User user : users) {
String name = user.getName();
if(name!= null ){
names.add(name);
}
String nickname = user.getNickname();
if(nickname != null){
names.add(nickname);
}
}
Although the code is longer , But it's easier to understand .
You can also encapsulate this part of the logic into a sub function and give a meaningful name .
/** * Get name and nickname */
private List<String> getNamesAndNickNames(List<User> users) {
List<String> names = new LinkedList<>();
for (User user : users) {
String name = user.getName();
if (name != null) {
names.add(name);
}
String nickname = user.getNickname();
if (nickname != null) {
names.add(nickname);
}
}
return names;
}
Call directly when using :
List<String> names = getNamesAndNickNames(users);
In this way, the outer function can clearly know the intention of this part of logic , You don't need to look at so much code at all , Readability is greatly improved .
5、 ... and 、 reflection
Too much of a good thing , We use Lambda When the expression , Be sure not to ignore readability .
Lambda There is nothing wrong with the expression , The fault is that many people abuse Lambda expression .
We should pay attention to making trade-offs in the coding process , Master the degree .
This paper briefly discusses Lambda The pros and cons of expressions , Give yourself some ways to crack .
Hopefully that helped .
Of course , There may be many solutions , Welcome to add your comments. .
I hope you can strive to be a programmer with pursuit .
It's not easy to create , If this article helps you , Welcome to thumb up 、 Collection and attention , Your support and encouragement , It's the biggest driving force of my creation .
边栏推荐
- Demonstration description of integrated base scheme
- Mysql中常见的锁
- 树莓派GPIO引脚控制红绿灯与轰鸣器
- Hand tear - sort
- 5g era is coming in an all-round way, talking about the past and present life of mobile communication
- 【leetcode】34. Find the first and last positions of elements in a sorted array
- SQL: common SQL commands
- What is 5g industrial wireless gateway? What functions can 5g industrial wireless gateway achieve?
- Document declaration and character encoding
- Actual combat | use composite material 3 in application
猜你喜欢

Actual combat | use composite material 3 in application

Spring recruitment of Internet enterprises: Kwai meituan has expanded the most, and the annual salary of technical posts is up to nearly 400000

Qt插件之Qt Designer插件实现

整理了一份ECS夏日省钱秘籍,这次@老用户快来领走

JVM knowledge points

Finally got byte offer. The 25-year-old inexperienced perception of software testing is written to you who are still confused

Yyds dry inventory compiler and compiler tools

First acquaintance with string+ simple usage (II)

Installation and use of blue lake

The confusion I encountered when learning stm32
随机推荐
Thinkphp6 limit interface access frequency
Realizing deep learning framework from zero -- Introduction to neural network
A summary of common interview questions in 2022, including 25 technology stacks, has helped me successfully get an offer from Tencent
Microsoft Research Institute's new book "Fundamentals of data science", 479 Pages pdf
Typescript practice for SAP ui5
【leetcode】34. Find the first and last positions of elements in a sorted array
[JS event -- event flow]
Okcc why is cloud call center better than traditional call center?
C language guessing numbers game
Pytorch---使用Pytorch进行图像定位
Installation et utilisation du lac bleu
Pytoch --- use pytoch to predict birds
Three years of experience in Android development interview (I regret that I didn't get n+1, Android bottom development tutorial
Dare to go out for an interview without learning some distributed technology?
Recyclerview add header
Mysql中常见的锁
[tips] use Matlab GUI to read files in dialog mode
树莓派GPIO引脚控制红绿灯与轰鸣器
[JS -- map string]
初识P4语言
