Этот ответ был действительно вдохновлен другой ответ по Хешам Аттиа и может рассматриваться в качестве альтернативного решения вопроса.
Тем не менее, я также использую эту возможность, чтобы обсудить потенциальную проблему переполнения типа данных int
(см. Ниже).
Данное решение использует интерфейс Comparator.comparingLong()
, а не интерфейс Map.Entry.comparingByValue()
с comparator
.
(оба могут страдать от переполнения данных, если мы не достаточно внимательны - см. Тесты 2 и 3 ниже)
hm.entrySet()
.stream()
.sorted(Comparator.comparingLong(
e -> ((long) e.getValue()[0])+e.getValue()[1]
))
Вот полная программа испытаний, которая демонстрирует неудавшиеся тесты 2 и 3 середина.Правильный ответ сортировки должен быть e, b, c, a, d
(показано в испытании 4)
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class StreamSortedIntArray {
public static void main(String[] args) {
Map<String, int[]> hm = new HashMap<>();
hm.put("a", new int[]{3, 1});
hm.put("b", new int[]{1, 1});
hm.put("c", new int[]{2, 1});
hm.put("d", new int[]{Integer.MAX_VALUE, 1});
hm.put("e", new int[]{Integer.MIN_VALUE, 1});
// Test 1:
System.out.println("Test 1: hm before sorting: ");
hm.entrySet()
.stream()
.forEach(StreamSortedIntArray::printEntry);
// Test 2:
System.out.println("Test 2: hm after sort -- using Map.Entry.comparingByValue()");
hm.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue(
(v1, v2) -> v2[0] + v2[1] - v1[0] - v1[1]))
.forEach(StreamSortedIntArray::printEntry);
// Test 3: After sorting -- using Comparator.comparingLong()
// WITHOUT protection against data overflowing the int type
System.out.println("Test 3: hm after sorting: (using Comparator.comparingLong())");
System.out.println("WITHOUT protection against data overflowing the int type");
hm.entrySet()
.stream()
.sorted(Comparator.comparingLong(
e -> e.getValue()[0]+e.getValue()[1]
))
.forEach(StreamSortedIntArray::printEntry);
// Test 4: After sorting -- using Comparator.comparingLong()
// WITH protection against data overflowing the int type
System.out.println("Test 4: hm after sorting: (using Comparator.comparingLong())");
System.out.println("WITH protection against data overflowing the int type");
hm.entrySet()
.stream()
.sorted(Comparator.comparingLong(
// protection against overflowing the int type
// cast to long before the sum operation
e -> ((long) e.getValue()[0])+e.getValue()[1]
))
.forEach(StreamSortedIntArray::printEntry);
}
public static void printEntry(Map.Entry<String, int[]> e) {
String message
= String.format("%s: %20s; sum=%d"
, e.getKey()
, Arrays.toString(e.getValue())
, ((long)(e.getValue()[0])+e.getValue()[1]));
System.out.println(message);
}
}
Выход этой программы - Тест 4 показывает правильный ответ, но тесты 2 и 3 не:
Test 1: hm before sorting:
a: [3, 1]; sum=4
b: [1, 1]; sum=2
c: [2, 1]; sum=3
d: [2147483647, 1]; sum=2147483648
e: [-2147483648, 1]; sum=-2147483647
Test 2: hm after sort -- using Map.Entry.comparingByValue()
e: [-2147483648, 1]; sum=-2147483647
d: [2147483647, 1]; sum=2147483648
a: [3, 1]; sum=4
c: [2, 1]; sum=3
b: [1, 1]; sum=2
Test 3: hm after sorting: (using Comparator.comparingLong())
WITHOUT protection against data overflowing the int type
d: [2147483647, 1]; sum=2147483648
e: [-2147483648, 1]; sum=-2147483647
b: [1, 1]; sum=2
c: [2, 1]; sum=3
a: [3, 1]; sum=4
Test 4: hm after sorting: (using Comparator.comparingLong())
WITH protection against data overflowing the int type
e: [-2147483648, 1]; sum=-2147483647
b: [1, 1]; sum=2
c: [2, 1]; sum=3
a: [3, 1]; sum=4
d: [2147483647, 1]; sum=2147483648
опасность реализации компаратора простого вычитанием
Эта проблема показана в приведенной выше программе испытаний (Test 2), но и была предостерегал против в Oracle/Sun Tutorial на объекте заказа
https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
последнее замечание: Вы можете попытаться заменить окончательное возвращение заявление в компараторе с более простым:
возвратного e1.number() - e2.number();
Не делайте этого, если вы не являетесь абсолютно уверены, что ни у кого никогда не будет отрицательного номера сотрудника! Этот трюк делает вообще неработоспособным, потому что целочисленный тип со знаком не достаточно большой , чтобы представить разность двух произвольных значащих целых чисел. Если i равно большое положительное целое число, а j - большое отрицательное целое число, i-j будет переполнения и вернет отрицательное целое число. Полученный компаратор нарушает одно из четырех технических ограничений, которые мы продолжаем говорить о (транзитивность) и производит ужасные, тонкие ошибки. Это не теоретическая проблема ; люди обжигаются им.
Для этого вы должны написать свой собственный компаратор. –
Я новичок в компараторах, но должен ли я передать ему функцию, которая добавляет два int вместе в int []? –
'HashMap' не сохраняют никакого заказа. Если это то, что вы пытаетесь сделать, вам понадобится другая структура данных. Или вы просто пытаетесь перебирать записи в этом порядке? – Mureinik