Java 8 - настраиваемая сортировка на основе определенного заказа - PullRequest
0 голосов
/ 12 ноября 2018

Я хотел бы отсортировать список пользователей по их статусу, но порядок должен быть основан на порядке, который я установил.

Я хочу установить порядок списка,

Порядок должен быть 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));

Вот класс пользователя

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 , поскольку этот порядок не является ни в порядке возрастания, ни в порядке убывания.

Ответы [ 5 ]

0 голосов
/ 12 ноября 2018

Как вы упомянули, вам нужен пользовательский порядок, а это означает, что вам нужно где-то определить этот порядок в HashMap <<strong> Status, Rank >> или одним простым способом добавить еще один атрибут, скажем Integer 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 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 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); 
    } 
} 
0 голосов
/ 12 ноября 2018

Вы можете попробовать сделать это шаг за шагом

//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));
0 голосов
/ 12 ноября 2018

Одним из подходов может быть удержание списка в нужном порядке и сортировка пользователей по его индексу:

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));
0 голосов
/ 12 ноября 2018

Предполагая, что 1, 0 и 5 будут единственными значениями status, AJNeufeld сделал отличную оценку в их комментарии ; они заявили, что вы можете использовать уравнение для отображения каждого значения в порядке возрастания. В этом случае уравнение будет (x - 1)^2, где x - это значение status:

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

Если вы напечатаете содержимое user после вызова вышеуказанного фрагмента, вы получите:

[Пользователь [имя пользователя = A, статус = 1], Пользователь [имя пользователя = D, статус = 1], Пользователь [имя пользователя = C, статус = 0], Пользователь [имя пользователя = F, статус = 0], Пользователь [ имя пользователя = B, статус = 5], пользователь [имя пользователя = E, статус = 5]]

0 голосов
/ 12 ноября 2018

Если вы не против использовать 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));
...