Talking about Java8 Comparator again

1. Get started

I talked about the Java8 Comparator skills before . Through the Java8 Comparator provides some methods, we can easily construct a custom Comparator.

It is very practical in some common sorting operations. At that time, I thought that I had absolutely no dead ends to understand Java8's Comparator, until I encountered a problem of comparing according to the properties of nested objects.

For example, I have a list of User objects, and now I want to sort them in reverse order according to the time attribute in the Info object in the User object. The sample code is as follows:

private static List<User> getUserList() {
        LinkedList<User> users = new LinkedList<>();
        users.add(new User(1, 25, new Info(9)));
        users.add(new User(2, 26, new Info(8)));
        users.add(new User(3, 30, new Info(7)));
        users.add(new User(4, 28, new Info(6)));
        return users;
    }

    private static final class User {
        private Integer id;
        private int age;
        private Info info;

        public User(Integer id, int age, Info info) {
            this.id = id;
            this.age = age;
            this.info = info;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Info getInfo() {
            return info;
        }

        public void setInfo(Info info) {
            this.info = info;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", age=" + age +
                    ", info=" + info +
                    '}';
        }
    }

    private static final class Info {
        private long time;

        public Info(long time) {
            this.time = time;
        }

        public long getTime() {
            return time;
        }

        public void setTime(long time) {
            this.time = time;
        }

        @Override
        public String toString() {
            return "Info{" +
                    "time=" + time +
                    '}';
        }
    }

Give it a try and see which of the following is correct:

userList.sort(Comparator.comparingLong(user -> user.getInfo().getTime()));
userList.sort(Comparator.comparingLong(user -> user.getInfo().getTime()).reversed());
userList.sort(Comparator.comparing(user -> user.getInfo().getTime()).reversed());
userList.sort(Comparator.comparingLong(User::getInfo::getTime()));

The answer is not correct, I don't want to play with you, but this can verify your own understanding of lambda expressions, :: and Java.

Before solving this problem, let's first understand Java's :: operator.

2. Method reference::

  1. Static method (static) reference: class name:: static method name such as: Helper::doSomething
  2. Object instance method reference: object instance name (or class name)::instance method name such as: System.out::println
  3. Object's superclass method reference: super::methodname
  4. Class constructor reference: class name::new such as: HashMap::new
  5. Array constructor reference: class name[]::new such as: String[]:new

3. comparingLong

Next, let's take a look at the Comparator's comparingLong method.

public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
}

Among them, ToLongFunction is a functional interface. In fact, it can be regarded as a lambda expression, which is a function that obtains a long type from a T object for comparison.

@FunctionalInterface
public interface ToLongFunction<T> {
    long applyAsLong(T value);
}

Note that the comparingLong method returns a Comparator, and the Comparator itself is also a functional interface, which is equivalent to wrapping a layer.

so:

userList.sort(Comparator.comparingLong(User::getInfo::getTime()));

There must be a problem, because User::getInfo has become a Comparator and is no longer a User object.

4. comparing

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

The same is true for comparing, it just wraps a more general Function into a Comparator, but this Function is a bit special, and its return parameter type must implement Comparable, otherwise it cannot be packaged.

Of course, primitive types like int are also possible, and will be automatically wrapped into the corresponding wrapper class.

so:

userList.sort(Comparator.comparing(user -> user.getInfo().getTime()).reversed());

It is definitely not possible, because the reversed method is not a static method, the problem is that the following one does not work either, which confuses me:

userList.sort(Comparator.comparing(user -> user.getInfo().getTime())::reversed);

The following is no problem, the problem is that the default is ascending order.

userList.sort(Comparator.comparingLong(user -> user.getInfo().getTime()));

Why do I need to pass anonymous inner class to achieve it?

Of course, no, because Comparator also has a method like this:

public static <T, U> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor,Comparator<? super U> keyComparator)

userList.sort(Comparator.comparing(user -> user.getInfo().getTime(), Comparator.reverseOrder()));

Get the job done!

5. Complete sample code

import org.junit.Test;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class ComparatorBTest {

    @Test
    public void comparingKey() {
        List<User> userList = getUserList();
        userList.sort(Comparator.comparing(User::getAge));
        System.out.println(userList);
    }

    @Test
    public void comparingComponent() {
        List<User> userList = getUserList();
        userList.sort(Comparator.comparing(User::getId).reversed().thenComparing(User::getAge));
        System.out.println(userList);
    }

    @Test
    public void comparing() {
        List<User> userList = getUserList();
//        userList.sort(Comparator.comparingLong(user -> user.getInfo().getTime()));
//        userList.sort(Comparator.comparingLong(user -> user.getInfo().getTime()).reversed());
//        userList.sort(Comparator.comparingLong(User::getInfo::getTime()));
//        userList.sort(Comparator.comparing(user -> user.getInfo().getTime()).reversed());
        userList.sort(Comparator.comparing(user -> user.getInfo().getTime(), Comparator.reverseOrder()));

        userList.forEach(System.out::println);
    }


    private static List<User> getUserList() {
        LinkedList<User> users = new LinkedList<>();
        users.add(new User(1, 25, new Info(9)));
        users.add(new User(2, 26, new Info(8)));
        users.add(new User(3, 30, new Info(7)));
        users.add(new User(4, 28, new Info(6)));
        return users;
    }

    private static final class User {
        private Integer id;
        private int age;
        private Info info;

        public User(Integer id, int age, Info info) {
            this.id = id;
            this.age = age;
            this.info = info;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Info getInfo() {
            return info;
        }

        public void setInfo(Info info) {
            this.info = info;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", age=" + age +
                    ", info=" + info +
                    '}';
        }
    }

    private static final class Info {
        private long time;

        public Info(long time) {
            this.time = time;
        }

        public long getTime() {
            return time;
        }

        public void setTime(long time) {
            this.time = time;
        }

        @Override
        public String toString() {
            return "Info{" +
                    "time=" + time +
                    '}';
        }
    }
}
{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324107573&siteId=291194637