Как отсортировать список в указанном порядке?

Объект OrderEntry:

@Data
public static class OrderEntry {
    private String entryNumber;
    // giftFlag=0 means the orderEntry is a normal entry
    // giftFlag=1 means the orderEntry is a gift entry
    private Integer giftFlag;
    private List<String> sourceEntryNumbers;
}

GiftFlag=0 означает, что orderEntry является обычной записью.

GiftFlag=1 означает, что orderEntry является записью о подарке.

У меня есть список orderEntry, он содержит обычную запись и запись подарка. Теперь я хочу отсортировать список orderEntry с указанным порядком, сначала обычную запись, если в этой обычной записи есть запись подарка, то во вторую очередь запись подарка.

обновлять: Я хочу отсортировать не-подарки по номеру записи и отслеживать подарки.

Это не сортировка по нескольким полям, и в ней нельзя использовать методы Comparator compare и thenComparing.

Как отсортировать этот список? Любая помощь?

@Test
public void testSort() {
    OrderEntry orderEntry1 = new OrderEntry();
    orderEntry1.setEntryNumber("1");
    orderEntry1.setGiftFlag(0);
    OrderEntry orderEntry2 = new OrderEntry();
    orderEntry2.setEntryNumber("2");
    orderEntry2.setGiftFlag(0);
    OrderEntry orderEntry3 = new OrderEntry();
    orderEntry3.setGiftFlag(1);
    // it means this gift orderEntry created from orderEntry1 and orderEntry2
    orderEntry3.setSourceEntryNumbers(Arrays.asList("1", "2"));

    OrderEntry orderEntry4 = new OrderEntry();
    orderEntry4.setEntryNumber("3");
    orderEntry4.setGiftFlag(0);
    OrderEntry orderEntry5 = new OrderEntry();
    orderEntry5.setGiftFlag(1);
    // it means this gift orderEntry created from orderEntry3
    orderEntry5.setSourceEntryNumbers(Arrays.asList("3"));

    OrderEntry orderEntry6 = new OrderEntry();
    orderEntry6.setEntryNumber("4");
    orderEntry6.setGiftFlag(0);
    OrderEntry orderEntry7 = new OrderEntry();
    orderEntry7.setGiftFlag(1);
    // it means this gift orderEntry created from orderEntry3
    orderEntry7.setSourceEntryNumbers(Arrays.asList("4"));

    OrderEntry orderEntry8 = new OrderEntry();
    orderEntry8.setEntryNumber("8");
    orderEntry8.setGiftFlag(0);

    List<OrderEntry> list = new ArrayList<>();
    list.add(orderEntry1);
    list.add(orderEntry2);
    list.add(orderEntry4);
    list.add(orderEntry6);
    list.add(orderEntry3);
    list.add(orderEntry5);
    list.add(orderEntry7);
    list.add(orderEntry8);

    System.out.println(JSON.toJSONString(list));
    // output:
    // [{"entryNumber":"1","giftFlag":0},{"entryNumber":"2","giftFlag":0},{"entryNumber":"3","giftFlag":0},{"entryNumber":"4","giftFlag":0},{"giftFlag":1,"sourceEntryNumbers":["1","2"]},{"giftFlag":1,"sourceEntryNumbers":["3"]},{"giftFlag":1,"sourceEntryNumbers":["4"]},{"entryNumber":"8","giftFlag":0}]

    // expected output:
    // [{"entryNumber":"1","giftFlag":0},{"entryNumber":"2","giftFlag":0},{"giftFlag":1,"sourceEntryNumbers":["1","2"]},{"entryNumber":"3","giftFlag":0},{"giftFlag":1,"sourceEntryNumbers":["3"]},{"entryNumber":"4","giftFlag":0},{"giftFlag":1,"sourceEntryNumbers":["4"]},{"entryNumber":"8","giftFlag":0}]
}

«и не может использовать методы сравнения и сравнения компаратора» - почему бы и нет? Они здесь именно для этого.

Sören 15.05.2024 11:03

В объекте списка есть обычная запись и запись о подарке, но запись о подарке имеет дополнительное свойство sourceEntryNumbers, это означает, что запись о подарке создана из обычной записи. Например, купи один, получи один бесплатно.

Holinc 15.05.2024 11:39

И я хочу отсортировать список с обычным порядком ввода и порядком ввода подарков. Исходный список, например: NormalEntry1, NormalEntry2, NormalEntry3, GiftEntry1, GiftEntry3, и список ожидаемых заказов, например: NormalEntry1, GiftEntry1, NormalEntry2, NormalEntry3, GiftEntry3.....

Holinc 15.05.2024 11:54

Ваша проблема в том, что вы не хотите сортировать их, просто сравнивая их попарно, а упорядочивая заказы без подарков по номеру записи и отслеживая подарки. Но я все еще не понимаю ваш пример в посте. Вы ожидаете, что orderEntry3 появится после orderEntry1 и orderEntry2.

Valerij Dobler 15.05.2024 13:27

Хотите ли вы поставить подарки в очередь сразу после самой часто упоминаемой обычной записи?

Valerij Dobler 15.05.2024 13:34

Этот вопрос ненамного лучше вашей оригинальной попытки.

Basil Bourque 15.05.2024 16:22

@ValerijDobler Да, заказывая заказы без подарков по номеру записи и отслеживая подарки.

Holinc 15.05.2024 16:38

@Holinc, посмотри мой ответ. Достигает ли это того, что вы ищете?

Valerij Dobler 15.05.2024 16:39
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
8
98
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы говорите, что не можете использовать comparing и thenComparing, но можете. Предоставляемая вами функция не обязательно должна быть ссылкой на метод.

Предполагая, что giftFlag не имеет значения NULL и entryNumber не имеет значения NULL для записей о подарках:

Comparator<OrderEntry> entryComparator = Comparator.comparing(OrderEntry::getGiftFlag)
        .thenComparing(entry -> entry.getGiftFlag() == 1 ? entry.getEntryNumber() : "");

Это позволит обычным записям предшествовать записям о подарках. Каждая обычная запись получит тот же результат для второго сравнения, и это нормально, если вы не начнете использовать TreeSet. В результате обычные записи идут первыми в исходном порядке, а за ними следуют подарочные записи, упорядоченные по номеру записи.

Альтернативно можно использовать обычный компаратор:

Comparator<OrderEntry> entryComparator = (entry1, entry2) -> {
    int result = entry1.getGiftFlag().compareTo(entry2.getGiftFlag());
    if (result == 0 && entry1.getGiftFlag() == 1) {
        // entry2.getGiftFlag() == 1 as well
        result = entry1.getEntryNumber().compareTo(entry2.getEntryNumber());
    }
    // else both have getGiftFlag() == 0, or they have different gift flags
    return result;
}

Несвязанный совет: используйте перечисление для giftFlag. GiftFlag.NORMAL понятнее, чем 0.

public enum GiftFlag {
    NORMAL,
    GIFT,
}

В объекте списка есть обычная запись и запись о подарке, но запись о подарке имеет дополнительное свойство sourceEntryNumbers, это означает, что запись о подарке создана из обычной записи. Например, купите один, получите один бесплатно. И я хочу отсортировать список с обычным порядком ввода и порядком ввода подарков. Исходный список, например: normalEntry1, normalEntry2, normalEntry3, giftEntry1, giftEntry3, и список ожидаемых заказов, например: normalEntry1, giftEntry1, normalEntry2, normalEntry3, giftEntry3.....

Holinc 15.05.2024 12:10

В моем случае это не работает.

Holinc 15.05.2024 12:11

Вы можете добавить функцию CompareTo в свой класс OrderEntry.

public static class OrderEntry implements Comparable<OrderEntry> {
    private String entryNumber;
    // giftFlag=0 means the orderEntry is a normal entry
    // giftFlag=1 means the orderEntry is a gift entry
    private Integer giftFlag;
    private List<String> sourceEntryNumbers;
    
    @Override
    public int compareTo(OrderEntry other) {
        return Integer.compare(this.giftFlag, other.giftFlag);
    }
}

@Test
public void testSort() {
    ...

    Collections.sort(list);
    System.out.println(JSON.toJSONString(list));
}

Это не то, чего я хочу.

Holinc 15.05.2024 12:05
Ответ принят как подходящий

Если я вас правильно понял, вы хотите, чтобы gift orders следовал за самым высоким normal orders, на который они ссылаются.

Затем мы можем добавить метод, возвращающий номер записи или последнюю запись источника, и использовать его в методе сравнения. Также нам нужно выполнить сортировку по GiftFlag, потому что мы хотим, чтобы номер записи «2» располагался перед исходным списком EntryNumbers List.of («1», «2).

@Data
public class OrderEntry implements Comparable<OrderEntry>{
    private String entryNumber;
    // giftFlag=0 means the orderEntry is a normal entry
    // giftFlag=1 means the orderEntry is a gift entry
    private Integer giftFlag;
    private List<String> sourceEntryNumbers;

    // can be omitted, it you know that the sourceEntryNumbers are sorted already
    public void setSourceEntryNumbers(List<String> sourceEntryNumbers) {
        this.sourceEntryNumbers = sourceEntryNumbers.stream().sorted().toList();
    }

    private String getEntryNumberForSorting() {
        if (entryNumber != null)
        //if (giftFlag == 1) // alternatively
        {
            return entryNumber;
        }
        //return sourceEntryNumbers.getLast(); // since java 21
        return sourceEntryNumbers.get(sourceEntryNumbers.size() - 1);
    }

    @Override
    public int compareTo(OrderEntry o) {
        return Comparator.comparing(OrderEntry::getEntryNumberForSorting)
                .thenComparing(OrderEntry::getGiftFlag)
                .compare(this, o);
    }
}

и затем вы можете отсортировать их, например. нравиться

final var orderedOrders = orders.stream()
                .sorted()
                .toList();

Элегантный способ решить мою проблему. Я публикую свой ответ, добавляя поле sortIndex для его решения. Не могли бы вы помочь мне посмотреть, все работает хорошо?

Holinc 15.05.2024 16:59

Как я уже упоминал ниже вашего ответа, это не сработает. Иногда заказ на подарок сортировался в другое место, чем вы упомянули в вопросе, о котором вы хотели бы.

Valerij Dobler 15.05.2024 17:29

Я пробую свой путь, как показано ниже. Добавляем поле sortIndex для сортировки.

@Test
public void testSort() {
    OrderEntry orderEntry1 = new OrderEntry();
    orderEntry1.setEntryNumber("1");
    orderEntry1.setGiftFlag(0);
    OrderEntry orderEntry2 = new OrderEntry();
    orderEntry2.setEntryNumber("2");
    orderEntry2.setGiftFlag(0);
    OrderEntry orderEntry3 = new OrderEntry();
    orderEntry3.setGiftFlag(1);
    // it means this gift orderEntry created from orderEntry1 and orderEntry2
    orderEntry3.setSourceEntryNumbers(Arrays.asList("1", "2"));

    OrderEntry orderEntry4 = new OrderEntry();
    orderEntry4.setEntryNumber("3");
    orderEntry4.setGiftFlag(0);
    OrderEntry orderEntry5 = new OrderEntry();
    orderEntry5.setGiftFlag(1);
    // it means this gift orderEntry created from orderEntry3
    orderEntry5.setSourceEntryNumbers(Arrays.asList("3"));

    OrderEntry orderEntry6 = new OrderEntry();
    orderEntry6.setEntryNumber("4");
    orderEntry6.setGiftFlag(0);
    OrderEntry orderEntry7 = new OrderEntry();
    orderEntry7.setGiftFlag(1);
    // it means this gift orderEntry created from orderEntry3
    orderEntry7.setSourceEntryNumbers(Arrays.asList("4"));

    OrderEntry orderEntry8 = new OrderEntry();
    orderEntry8.setEntryNumber("8");
    orderEntry8.setGiftFlag(0);

    List<OrderEntry> list = new ArrayList<>();
    list.add(orderEntry1);
    list.add(orderEntry2);
    list.add(orderEntry4);
    list.add(orderEntry6);
    list.add(orderEntry3);
    list.add(orderEntry5);
    list.add(orderEntry7);
    list.add(orderEntry8);

    System.out.println(JSON.toJSONString(list));

    // setting value into sortIndex field
    list.forEach(item -> {
        if (CollectionUtils.isNotEmpty(item.getSourceEntryNumbers())) {
            List<String> sourceEntryNumbers = item.getSourceEntryNumbers().stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
            item.setSortIndex(sourceEntryNumbers.get(0));
        } else {
            item.setSortIndex(item.getEntryNumber());
        }
    });

    // sorting by sortIndex field again
    List<OrderEntry> result = list.stream().sorted(Comparator.comparing(OrderEntry::getSortIndex)).collect(Collectors.toList());

    // output result
    System.out.println(JSON.toJSONString(result));
}

@Data
public static class OrderEntry {
    private String entryNumber;
    private Integer giftFlag;
    private List<String> sourceEntryNumbers;

    // add sortIndex field to sort
    private String sortIndex;
}

Если вы выберете sourceEntryNumbers.get(0) в качестве индекса, вы будете сортировать orderEntry3 до или после orderEntry2, что также неоптимально.

Valerij Dobler 15.05.2024 16:57

Должно сработать, я уже отсортировал sourceEntryNumbers с помощью Comparator.reverseOrder() List<String> sourceEntryNumbers = item.getSourceEntryNumbers().stream().sorted(Comparator.reve‌​rseOrder()).collect(‌​Collectors.toList())‌​;

Holinc 15.05.2024 17:51

В данном случае это сработало случайно. Просто попробуйте сделать это с обратным упорядоченным списком: orderEntry8,orderEntry7,orderEntry6,orderEntry5,orderEntry4,‌​orderEntry3,orderEnt‌​ry2,orderEntry1, тогда вы увидите, что под индексом 1 будет orderEntry3, хотя вы ожидаете, что за orderEntry2 следует orderEntry3, а не наоборот.

Valerij Dobler 15.05.2024 18:14

Вот почему вам нужна часть .thenComparing(OrderEntry::getGiftFlag).

Valerij Dobler 15.05.2024 18:20

Другие вопросы по теме