Comparator .comparing () странное поведение / не работает, как ожидалось - PullRequest
5 голосов
/ 03 апреля 2019

Насколько я знаю, Comparator.comparingInt() должен сортироваться в порядке возрастания, а Comparator.comparingInt().reversed - в порядке убывания.Но я нашел сценарий, где это наоборот.

Это лучше объяснить на примере.Ниже приведен мой код.

Класс суммы:

class Amount
{
    int lineNum;
    int startIndex;
    Double value;
//Getters , setters and toString.
}

Основной метод:

public static void main( String[] args )
{
    List<Amount> amounts = new ArrayList<>();
    amounts.add( new Amount( 1.0, 5, 10 ) ); //LINE_NUM 5
    amounts.add( new Amount( 3.0, 9, 30 ) );
    amounts.add( new Amount( 2.0, 3, 40 ) );
    amounts.add( new Amount( 9.0, 5, 20 ) ); //LINE_NUM 5
    amounts.add( new Amount( 6.0, 1, 50 ) );
    amounts.add( new Amount( 4.0, 5, 20 ) ); //LINE_NUM 5
    System.out.println( ".............BEFORE SORTING.........." );
    amounts.forEach( System.out::println );


    amounts.sort( 
                 Comparator.comparingInt( Amount::getLineNum )   //NOTE THIS
        .           .thenComparingInt( Amount::getStartIndex ).reversed()
                      .thenComparingDouble( Amount::getValue ) );

    System.out.println( "\n\n.............AFTER SORTING.........." );

    amounts.forEach( System.out::println );
}

Я хотел, чтобы список сумм был отсортирован по возрастанию lineNum, по убыванию startIndex и по возрастанию значения.

Итак, я ожидал, что это так.

............. ПОСЛЕ СОРТИРОВКИ .......... ( ОЖИДАНИЕ )

Сумма [lineNum = 1, startIndex = 50, значение = 6.0]

Сумма [lineNum = 3, startIndex = 40, значение = 2.0]

Сумма [lineNum = 5, startIndex = 20, значение = 4.0]

Сумма [lineNum = 5, startIndex = 20, значение = 9.0]

Сумма [lineNum = 5, startIndex =10, значение = 1,0]

Сумма [lineNum = 9, startIndex = 30, значение = 3,0]

............. ПОСЛЕ СОРТИРОВКИ .......... ( ACTUAL )

Сумма [lineNum = 9, startIndex = 30, значение = 3.0]

Сумма [lineNum = 5, startIndex =20, значение = 4,0]

Сумма [lineNum = 5, startIndex = 20, значение = 9,0]

Сумма [lineNum = 5, startIndex = 10, значение = 1,0]

Сумма [lineNum = 3, startIndex = 40,value = 2.0]

Сумма [lineNum = 1, startIndex = 50, value = 6.0]

Все было правильно, за исключением lineNum order .Суммы были отсортированы по убыванию lineNumber , в то время как я ожидал, что они будут в порядке возрастания .

Результаты быликак и ожидалось, когда я изменил Comparator на следующий

amounts.sort(
    Comparator.
    comparingInt( Amount::getLineNum ).reversed()
    .thenComparingInt( Amount::getStartIndex ).reversed()
    .thenComparingDouble( Amount::getValue ) );

, что странно, поскольку comparingInt( Amount::getLineNum ).reversed() должен был сортировать суммы по убыванию номера строки.

Еще одна вещь, которую я заметил, это Сравнение по StartIndex работает как положено.Но Сравнение по lineNumber часть не является.

Может кто-нибудь объяснить это?

Ответы [ 3 ]

10 голосов
/ 03 апреля 2019

Проще понять, что происходит, если вы помещаете каждый вызов в строку:

Comparator.comparingInt(Amount::getLineNum)
    .thenComparingInt(Amount::getStartIndex)
    .reversed()
    .thenComparingDouble(Amount::getValue)

Это reversed() возвращает компаратор, который инвертирует результаты вызываемого компаратора ...«компаратор, который сначала сравнивает номер строки, затем начальный индекс».Это не значит, что он заключен в квадратные скобки только для объема предыдущего вызова thenComparingInt(), как выглядело ваше предыдущее форматирование.

Вы можете сделать это следующим образом:

Comparator.comparingInt(Amount::getLineNum)
    .thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
    .thenComparingDouble(Amount::getValue)

На это указывает только обратное сравнение начального индекса.

1 голос
/ 03 апреля 2019

Поместите вызов reversed () внутрь thenComparing:

   Comparator.comparingInt(Amount::getLineNum)

   .thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
   .thenComparingDouble( Amount::getValue );
1 голос
/ 03 апреля 2019

Из документов :

reversed(): возвращает компаратор, который налагает обратный порядок в этом компараторе.

thenComparing(): возвращает компаратор лексикографического порядка с другим компаратором. Если этот компаратор считает два элемента равными, т. Е. Сравнение (a, b) == 0, для определения порядка используется другой.

Каждый шаг создает новый компаратор на основе предыдущего. Таким образом, метод reversed() создает обращенный компаратор

Comparator.comparingInt(Amount::getLineNum).thenComparingInt(Amount::getStartIndex)

Чтобы перевернуть только второй, вы должны обернуть его в собственный компаратор:

.thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())

В вашем втором решении результат верен, потому что вы фактически дважды меняете первое условие:

Comparator.comparingInt(Amount::getLineNum).reversed() // reverses one time
    .thenComparingInt(Amount::getStartIndex).reversed() // reverses all before (also the first one)

Итак, полное решение будет выглядеть так:

Comparator.comparingInt(Amount::getLineNum)
    .thenComparing(Comparator.comparingInt(Amount::getStartIndex).reversed())
    .thenComparingDouble(Amount::getValue)
...