Java 8 - 根据特定顺序自定义排序

Java 8 - customised sort based on specific order

我想根据用户状态对用户列表进行排序,但顺序必须基于我设置的顺序。

我想设置列表的顺序,

顺序应该是1,0,5。我们还应该记住订购用户名。

List<User> users = new ArrayList();
         users.add(new User("A", 1));
         users.add(new User("B", 5));
         users.add(new User("C", 0));
         users.add(new User("D", 1));
         users.add(new User("E", 5));
         users.add(new User("F", 0));

这是用户class

public class User {
         private String username;
         private Integer status;
     }

应该是这样的

[
    {
      "username": "A",
      "status": 1
    },
    {
       "username": "D",
       "status": 1
    },
    {
       "username": "C",
       "status": 0
    },
    {
       "username": "F",
       "status": 0
    },
    {
       "username": "B",
       "status": 5
    },
    {
       "username": "E",
       "status": 5
    }
]

我不确定是否可以使用Comparator.comparing,因为这个既不是升序也不是降序。

如果你不介意在你的项目中使用Guava,你可以使用Ordering.explicit:

users.sort(Ordering.explicit(1, 0, 5).onResultOf(User::getStatus));

如果您还想按名称排序,请添加 thenComparing:

users.sort(Ordering
        .explicit(1, 0, 5)
        .onResultOf(User::getStatus)
        .thenComparing(User::getUsername));

一种方法是按照您想要的顺序保存一个列表,并根据其索引对用户进行排序:

final List<Integer> order = Arrays.asList(1, 0, 5);
users.sort(
    Comparator.comparing((User u) -> order.indexOf(u.getStatus()))
              .thenComparing(User::getUsername));

请注意,虽然这种方法对于少量状态(如您目前拥有的)应该是合理的,但如果有大量状态并且您需要执行 O(n)每次搜索。一种性能更好的方法(尽管可以说不是那么圆滑)是使用地图:

final Map<Integer, Integer> order = new HashMap<>();
order.put(1, 0);
order.put(0, 1);
order.put(5 ,2);
users.sort(Comparator.comparing((User u) -> order.get(u.getStatus()))
                     .thenComparing(User::getUsername));

假设 105 将是 status 的唯一值,AJNeufeld 在 中提出了一个很好的观点;他们说您可以使用方程式将每个值映射到升序。在这种情况下,等式将是 (x - 1)^2,其中 xstatus 的值:

users.sort(Comparator.comparingDouble(user -> Math.pow(user.getStatus() - 1, 2)));

如果您在调用上述代码段后打印 user 的内容,您将得到:

[User [username=A, status=1], User [username=D, status=1], User [username=C, status=0], User [username=F, status=0], User [username=B, status=5], User [username=E, status=5]]

你可以尝试一步一步来

//order define here
List<Integer> statusOrder= Arrays.asList(1,0,5,2);

//define sort by status
Comparator<User> byStatus = (u1, u2) -> {
    return Integer.compare(statusOrder.indexOf(u1.getStatus()), statusOrder.indexOf(u2.getStatus()));
};

//define sort by name
Comparator<User> byName = Comparator.comparing(User::getUsername);

//actualy sort
users.sort(byStatus.thenComparing(byName));

正如您所提到的,您需要自定义排序,这意味着您需要在 HashMap<Status,Rank>> 中的某处定义排序,或者通过一种简单的方法添加一个属性说 整数排名; 并且您可以根据状态属性的顺序定义排名,例如 users.add(new User("A", 1,0));这里状态 1 按顺序排序,其等级 = 0。然后你可以在等级属性.

上使用Comparator

例如:

public class User {
    public String username;
    public Integer status;
    public Integer rank;

    public User(String username, Integer status, Integer rank) 
    { 
        this.username = username; 
        this.status = status; 
        this.rank = rank;
    } 
}

比较器class:

class SortByRank implements Comparator<User> 
{ 
    // Used for sorting in ascending order of 
    // rank number 
    public int compare(User a, User b) 
    { 
        return a.rank - b.rank; 
    } 
} 

主要 Class :

class Main 
{ 
    public static void main (String[] args) 
    { 
         List<User> users = new ArrayList();
         users.add(new User("A", 1, 0));
         users.add(new User("B", 5, 2));
         users.add(new User("C", 0, 1));
         users.add(new User("D", 1, 0));
         users.add(new User("E", 5, 2));
         users.add(new User("F", 0, 1));

        System.out.println("Unsorted"); 
        for (int i=0; i<users.size(); i++) 
            System.out.print(users.get(i).username); 

        Collections.sort(users, new SortByRank()); 

        System.out.println("\nSorted by Rank"); 
        for (int i=0; i<users.size(); i++) 
            System.out.print(users.get(i).username); 
    } 
}