Java 8 - настраиваемая сортировка на основе определенного заказа

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

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

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

Ответ 1

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

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));

Ответ 2

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

Ответ 3

Предполагая, что 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, status = 5], Пользователь [username = E, status = 5]]

Ответ 4

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

//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));

Ответ 5

Как вы уже упоминали, вам нужен пользовательский порядок, а это значит, что вам нужно где-то определить этот порядок в HashMap < 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); 
    } 
}