How write universal comparator which can make sorting through all necessary fields?

做~自己de王妃 提交于 2020-01-01 00:35:06

问题


I needed to implement sorting on all fields of my class I wrote a comparator for each field in my class. But I had to write a individual comparator for each field. I think that it is not very correctly. How write for my class a single universal comparator which can make sorting through all fields?

My entity:

public class User {
    private long id;
    private String name;
    private int age;
 .....................

My comparators:

public class UserComparatorById implements Comparator<User> {   
    public int compare(User user1, User user2) {        
       int result = (int)(user1.getId() - user2.getId());
       if (result != 0) return (int)(result/Math.abs(result));        
       result = user1.getName().compareTo (user2.getName());
       if (result != 0) return (int)(result/Math.abs(result));
       result = user1.getAge() - user2.getAge();
       return (result != 0) ? (int)(result/Math.abs(result)) : 0;
    }
}

public class UserComparatorByName implements Comparator<User> { 
    public int compare(User user1, User user2) {            
       int result = user1.getName().compareTo (user2.getName());
       if (result != 0) return (int)(result/Math.abs(result));
       result = (int)(user1.getId() - user2.getId());
       if (result != 0) return (int)(result/Math.abs(result));  
       result = user1.getAge() - user2.getAge();
       return (result != 0) ? (int)(result/Math.abs(result)) : 0;
    }
}

public class UserComparatorByAge implements Comparator<User> {  
    public int compare(User user1, User user2) {            
       int result = user1.getAge() - user2.getAge();
       if (result != 0) return (int)(result/Math.abs(result));
       result = (int)(user1.getId() - user2.getId());
       if (result != 0) return (int)(result/Math.abs(result));   
       result = user1.getName().compareTo (user2.getName());
       return (result != 0) ? (int)(result/Math.abs(result)) : 0;
    }
}

Sorting:

List<User> users = new ArrayList<User>();

    users.add(new User(5, "Frank", 28));
    users.add(new User(1, "Jorge", 19));
    users.add(new User(6, "Bill", 34));
    users.add(new User(3, "Michel", 17));
    users.add(new User(7, "Mark", 42));

    UserComparatorByName comparatorByName = new UserComparatorByName();
    Collections.sort(users, comparatorByName);

    UserComparatorByAge comparatorByAge = new UserComparatorByAge();
    Collections.sort(users, comparatorByAge);

回答1:


One implementation per field isn't that bad, however there is a lot of duplication in your code. You should have one comparator per exactly one field. If you want to compare based on several fields but in different order, wrap atomic comparators (Decorator pattern):

public abstract class AbstractComparator implements Comparator<User> {
    private final AbstractComparator next;

    public AbstractComparator(AbstractComparator next) {
        this.next = next;
    }

    public int compare(User user1, User user2) {
        int result = doCompare(user1, user2);
        if (result != 0) {
            return result;
        } else {
            return next != null? next.compare(user1, user2) : 0;
        }
    }

    public abstract int doCompare(User user1, User user2);
}

class ById extends AbstractComparator {
    public ById(AbstractComparator next) {
        super(next);
    }

    public int doCompare(User user1, User user2) {
        return (int) (user1.getId() - user2.getId());
    }
}

class ByName extends AbstractComparator {
    public ByName(AbstractComparator next) {
        super(next);
    }

    public int doCompare(User user1, User user2) {
        return user1.getName().compareTo(user2.getName());
    }
}

class ByAge extends AbstractComparator {
    public ByAge(AbstractComparator next) {
        super(next);
    }

    public int doCompare(User user1, User user2) {
        return user1.getAge() - user2.getAge();
    }
}

And usage:

Comparator<User> comp1 = new ById(new ByName(new ByAge(null)));
Comparator<User> comp2 = new ByAge(new ByName(new ById(null)));

comp1 first sorts by id and if it is equal, falls back to name and to age as a last resort. The API is pretty clear.

For convenience you should place all By* classes as static inner classes of User: User.ByName, User.ByAge, etc. or maybe with some factory methods: User.byName(User.byAge(null)). With a help of some static imports you can echieve pleasant:

Collections.sort(users, byName(byAge(byId(null))));

Alternatively have a look at CompareToBuilder from Commons Lang.




回答2:


Maybe the Bean Comparator and Group Comparator will help you out.



来源:https://stackoverflow.com/questions/8087330/how-write-universal-comparator-which-can-make-sorting-through-all-necessary-fiel

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!