我将HashMap定义为HashMap<String, int[]>
。值为int[]
,其中只有2个数字。我想要做的是通过这两个数字的总和对这个HashMap的条目进行排序。
这就是我所拥有的。我正在使用Java 8.我只需要添加我在ints
中对2 int[]
求和的部分,并将其作为一个数字处理,然后按照下面的方式排序,但我不知道如何添加该部分。
hm.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
答案 0 :(得分:3)
以下是Java 8 Comparator lambdas的解决方案:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
(v1, v2) -> v2[0] + v2[1] - v1[0] - v1[1]));
请注意,此解决方案存在溢出/下溢的风险,请参阅leeyuiwah answer以获得更好的解决方案。我们的想法是使用comparingLong
方法。
答案 1 :(得分:1)
我很确定您无法使用HashMap创建排序元素。 我的建议是使用另外两张地图:
listInner::contains
首先,您需要将hm地图复制到tempMap中,TreeMap中的自然顺序将按升序排序整数键。
在tempMap中获取排序结果后,可以复制到resultMap中以获得最终结果。
&#34;复印&#34;意味着您迭代旧地图,然后将(键,值)放在新地图中。
这种方法会花费你双倍内存,但它会以复杂度O(n lg n)运行。
如果您想使用Comparator,可以使用以下方法:
public class Combiner {
public static void main(String[] args) {
List<List<Integer>> mainList = new ArrayList<>();
mainList.add(Arrays.asList(1, 2));
mainList.add(Arrays.asList(4, 5));
mainList.add(Arrays.asList(7, 8));
mainList.add(Arrays.asList(6, 19));
mainList.add(Arrays.asList(2, 3, 5, 7));
System.out.println(combineList(new ArrayList<>(mainList)));
List<List<Integer>> result = mergeCollections(new ArrayList<>(mainList));
System.out.println(result);
}
public static <T> List<List<T>> combineList(List<List<T>> argList) {
List<List<T>> result = new ArrayList<>();
for (List<T> list : argList) {
//Copy the given list
List<T> addedList = new ArrayList<>(list);
result.add(addedList);
for (List<T> otherList : argList) {
if (list.equals(otherList)) continue;
//If at least one element is shared between the two lists
if (list.stream().anyMatch(otherList::contains)) {
//Add all elements that are exclusive to the second list
addedList.addAll(otherList.stream().map(t -> addedList.contains(t) ? null : t)
.filter(t -> t != null).collect(Collectors.toList()));
}
}
}
List<List<T>> del = new ArrayList<>();
for (int i = 0; i < result.size(); i++) {
for (int j = i + 1; j < result.size(); j++) {
List<T> list = result.get(j);
if (listEqualsUnOrdered(list, result.get(i))) {
//Modified this
del.add(result.get(i));
}
}
//Can't use listIterator here because of iterating starting at j + 1
result.removeAll(del);
}
//Recursion
if (!result.equals(argList)) {
result = combineList(result);
}
return result;
}
private static <T> boolean listEqualsUnOrdered(List<T> list1, List<T> list2) {
if (list1.size() != list2.size()) return false;
List<T> testOne = new ArrayList<>(list1);
testOne.removeAll(list2);
boolean testOnePassed = (testOne.size() == 0);
List<T> testTwo = new ArrayList<>(list2);
testTwo.removeAll(list1);
if (testTwo.size() == 0 && testOnePassed) return true;
return false;
}
}
答案 2 :(得分:1)
这个答案实际上是受到Hesham Attia的另一个答案的启发,可以作为问题的替代解决方案。
但是,我也借此机会讨论溢出int
数据类型的潜在问题(详见下文)。
此解决方案使用带有Comparator.comparingLong()
的界面keyExtractor
,而不是带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
通过简单减法实现比较器的危险
这个问题在上面的测试程序(测试2)中显示,但也是如此 在Oracle / Sun对象排序教程中警告
https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
最后一点:您可能想要替换最终的回报 比较器中的声明更简单:
return e1.number() - e2.number();
除非你是绝对的,否则不要这样做 确定没有人会有负面的员工编号!这个伎俩呢 通常不起作用,因为有符号整数类型不够大 表示两个任意有符号整数的差异。如果我是 一个大的正整数,j是一个大的负整数,i - j会 溢出并返回一个负整数。结果比较器 违反了我们一直在谈论的四项技术限制之一 (传递性)并产生可怕的,微妙的错误。 这不是 纯粹的理论关注;人们被它焚烧。
答案 3 :(得分:1)
更一般的解决方案,使用适当的比较器(它不会影响可能的溢出问题),这将适用于任何长度的数组:
1)因为您无法对不保留密钥顺序的HashMap
进行排序,我们需要创建新地图 - LinkedHashMap
:
Map<String, int[]> result = new LinkedHashMap<>();
2)自我排序:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
(o1, o2) -> Integer.compare(Arrays.stream(o1).sum(), Arrays.stream(o2).sum()))
)
.forEach(se -> result.put(se.getKey(), se.getValue()));
UPD:亲爱的@Holger建议使用Comparator.comparingInt(o -> Arrays.stream(o).sum())
看起来更紧凑,但做同样的工作。对我个人而言,我的版本看起来更容易理解,但Holger的版本更像是lambda-styler。
答案 4 :(得分:1)
最简单的恕我直言最好的方法是不使用Map.Entry比较方法,因为您不是通过键或值进行比较,而是使用派生值进行比较:
map.entrySet().stream()
.sorted(Comparator.comparing(e -> 0 - e.getValue()[0] - e.getValue[1]))
.forEach(<whatever>);
负值会产生代码建议你想要的颠倒顺序。