Don't write such divine code again!

Although the Stream provided by JDK8 is easy to use and Lambda is simple, it must not be abused. Let me give an example (desensitization has been done) that I have encountered in practice:

Map<Long, List<Student>> studentMap = students.stream().collect(Collectors.groupingBy(Student::getStudentNumber)).entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));

Who can understand? Is there no line feed format?

Map<Long, List<Student>> studentMap = students.stream().collect(Collectors.groupingBy(Student::getStudentNumber))      //This is to group students by student number
                .entrySet().stream().sorted(Map.Entry.comparingByKey())     //After grouping, the key values are used for sorting to form a new Stream
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,       //Then Map? I'm sorry. I can't read any more
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));

After the line feed is formatted, the previous stream operation can barely be understood. It's impossible to read the last lambda expression. I don't know what he wants to express at all.

But what if we really encounter such "God level" code? Fortunately, IDEA helps us. Move the mouse to the code and right-click to display the following menu:

Click "Show Context Actions" to display the following contents:

Select Replace Stream API chain with loop.

When I replace all Stream operations and abbreviated lambda expressions with "traditional" code, the code logic is as follows:

Map<Long, List<Student>> map = new HashMap<>();
//Group by step
for (Student student : students) {
    //The computeIfAbsent method is equivalent to the following code
    /*List<Student> list = map.get(student.getStudentNumber());
     if (list == null) {
         list = new ArrayList<>();
         map.put(list);
     }
     list.add(student)*/
    map.computeIfAbsent(student.getStudentNumber(), new Function<Long, List<Student>>() {
        @Override
        public List<Student> apply(Long k) {
            return new ArrayList<>();
        }
    }).add(student);
}
//Put the Entry element of Map into the List and sort
List<Map.Entry<Long, List<Student>>> toSort = new ArrayList<>();
for (Map.Entry<Long, List<Student>> integerListEntry : map
        .entrySet()) {
    toSort.add(integerListEntry);
}
toSort.sort(Map.Entry.comparingByKey());
//Use LinkedHashMap again to arrange by insertion order
Map<Long, List<Student>> studentkMap = new LinkedHashMap<>();
for (Map.Entry<Long, List<Student>> integerListEntry : toSort) {
    studentkMap.putIfAbsent(integerListEntry.getKey(), integerListEntry.getValue());
}

In this way, the code logic is clear. Isn't it actually to group the List by step s in the elements and arrange it in dictionary order? If you follow the initial Stream+Lambda expression, let alone optimization, even understanding is a problem. When we change the code to "traditional" code, the logic becomes clear.

Map<Long, List<Student>>> studentMap = new TreeMap<>();
for (Student student : students) {
    List<Student> list = map.get(student.getStudentNumber());
    if (list == null) {
        list = new ArrayList<>();
        map.put(list);
    }
    list.add(student)
}

Is it better to use Stream and Lambda properly?

Pay attention to the official account (CoderBuff) reply to "stream" to get the full version of Java8 Stream encoding real battle PDF.

This is a CoderBuff that can give programmers buff official account.

Tags: Java Lambda encoding

Posted on Sat, 21 Mar 2020 09:46:48 -0400 by stride-r