当前位置:网站首页>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 .
边栏推荐
- Spring recruitment of Internet enterprises: Kwai meituan has expanded the most, and the annual salary of technical posts is up to nearly 400000
- Pytorch---使用Pytorch进行鸟类的预测
- Wechat applet calculates the distance between the two places
- C语言:逻辑运算和判断选择结构例题
- Sorted out an ECS summer money saving secret, this time @ old users come and take it away
- Realizing deep learning framework from zero -- Introduction to neural network
- First acquaintance with string+ simple usage (II)
- Monkey测试
- Use a mask to restrict the input of the qlineedit control
- Which insurance company has a better product of anti-cancer insurance?
猜你喜欢
Learn more about materialapp and common attribute parsing in fluent
手撕——排序
Yolov5 network modification tutorial (modify the backbone to efficientnet, mobilenet3, regnet, etc.)
[C language] Dynamic Planning --- from entry to standing up
《动手学深度学习》(二)-- 多层感知机
office_ Delete the last page of word (the seemingly blank page)
[untitled]
go 包的使用
10 minutes to understand CMS garbage collector in JVM
Playing with concurrency: what are the ways of communication between threads?
随机推荐
C语言猜数字游戏
手撕——排序
【leetcode】74. Search 2D matrix
There is no prompt for SQL in idea XML, and the dialect setting is useless.
Common sense of cloud server security settings
The first practical project of software tester: web side (video tutorial + document + use case library)
Opencv learning example code 3.2.4 LUT
powershell_ View PowerShell function source code (environment variable / alias) / take function as parameter
Learn more about materialapp and common attribute parsing in fluent
go 分支与循环
uni-app - 实现获取手机验证码倒计时 60 秒(手机号+验证码登录功能)
Www2022 | know your way back: self training method of graph neural network under distribution and migration
[wireless image transmission] FPGA based simple wireless image transmission system Verilog development, matlab assisted verification
Pytorch yolov5 exécute la résolution de bogues à partir de 0:
Fingertips life Chapter 4 modules and packages
Which is better, industrial intelligent gateway or edge computing gateway? How to choose the right one?
Spring recruitment of Internet enterprises: Kwai meituan has expanded the most, and the annual salary of technical posts is up to nearly 400000
go 变量与常量
QT designer plug-in implementation of QT plug-in
2022-07-01: at the annual meeting of a company, everyone is going to play a game of giving bonuses. There are a total of N employees. Each employee has construction points and trouble points. They nee