Java 如何对HashMap进行排序';通过比较值(其中每个值都是int[])来设置条目?
我将HashMap定义为Java 如何对HashMap进行排序';通过比较值(其中每个值都是int[])来设置条目?,java,sorting,dictionary,java-8,comparator,Java,Sorting,Dictionary,Java 8,Comparator,我将HashMap定义为HashMap。该值是一个int[],其中正好有2个数字。我想做的是按照这两个数字的总和对HashMap的条目进行排序 这是我的。我正在使用Java8。我只需要在int[]中添加两个int相加的部分,并将其视为一个数字,然后像下面那样排序,但我不确定如何添加该部分 hm.entrySet().stream() .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) 我很确定不能用HashMap
HashMap
。该值是一个int[]
,其中正好有2个数字。我想做的是按照这两个数字的总和对HashMap的条目进行排序
这是我的。我正在使用Java8。我只需要在int[]
中添加两个int
相加的部分,并将其视为一个数字,然后像下面那样排序,但我不确定如何添加该部分
hm.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
我很确定不能用HashMap创建ordering元素。 我的建议是使用另外两张地图:
Map<Integer,String> tempMap = new TreeMap<Integer,String>();
Map<String,int []> resultMap = new LinkedHashMap<String,int[]>();
Map tempMap=newtreemap();
Map resultMap=newlinkedhashmap();
首先,您需要将hm映射复制到tempMap中,TreeMap中的自然顺序将按升序排列整数键
在tempMap中获得排序结果后,可以复制到resultMap中以获得最终结果
“复制”意味着您迭代旧映射,然后将(键、值)放入新映射
这种方法将花费您两倍的内存,但它将以O(n lg n)的复杂度运行
如果要使用比较器,可以使用以下方法:
hm.entrySet().stream().sorted(Map.Entry.comparingByValue(
new Comparator<int []>() {
public int compare(int [] a,int [] b) {
int sumA = a[0] + a[1];
int sumB = b[0] + b[1];
return sumA - sumB;
}
}));
hm.entrySet().stream().sorted(Map.Entry.comparingByValue(
新比较器(){
公共整数比较(整数[]a,整数[]b){
int sumA=a[0]+a[1];
int sumB=b[0]+b[1];
返回sumA-sumB;
}
}));
这个答案实际上受到了Hesham Attia的另一个答案的启发,可以作为这个问题的替代解决方案
不过,我也借此机会讨论了int
数据类型溢出的潜在问题(请参阅下文)
此解决方案使用接口Comparator.comparingLong()
和键提取器
,而不是接口映射.Entry.comparingByValue()
和比较器
(如果我们不够小心,两者都可能出现数据溢出的问题——请参见下面的测试2和测试3。)
这里是一个完整的测试程序,它演示了中间测试失败的2和3。排序的正确答案应该是
e、b、c、a、d
(如测试4所示)
通过简单的减法实现比较器的危险
上述测试程序(测试2)中显示了此问题,但也已解决
在Oracle/Sun对象排序教程中警告
最后一点注意:您可能会尝试替换最终返回
比较表中的语句更简单:
返回e1.number()-e2.number()
除非你完全同意,否则不要这样做
当然没有人会有一个负的员工号码!这个把戏很管用
通常不起作用,因为有符号整数类型不够大
表示两个任意有符号整数的差。如果我是
一个大的正整数,而j是一个大的负整数,i-j将
溢出并将返回一个负整数。结果比较器
违反了我们一直在谈论的四个技术限制之一
(及物性)并产生可怕、微妙的bug。这不是一个好主意
纯粹的理论关注;人们会被它烧伤
以下是使用Java 8 Comparator lambdas的解决方案:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
(v1, v2) -> v2[0] + v2[1] - v1[0] - v1[1]));
请注意,此解决方案存在溢出/下溢的风险,有关更好的解决方案,请参阅。我们的想法是使用
comparingLong
方法。更一般的解决方案,使用合适的比较器(不受可能的溢出问题的影响),它将适用于任何长度的数组:
1) 由于无法对不保留键顺序的HashMap
进行排序,我们需要创建新的map-LinkedHashMap
:
Map<String, int[]> result = new LinkedHashMap<>();
UPD:Dear@Holger建议使用
Comparator.comparingit(o->Arrays.stream(o.sum())
,它看起来更紧凑,但功能相同。就我个人而言,我的版本看起来更容易理解,但霍尔格的版本更容易理解。最简单也是最好的方法是不使用映射。条目比较方法,因为您不是按键或值进行比较,而是按派生值进行比较:
map.entrySet().stream()
.sorted(Comparator.comparing(e -> 0 - e.getValue()[0] - e.getValue[1]))
.forEach(<whatever>);
map.entrySet().stream()
.sorted(Comparator.comparing(e->0-e.getValue()[0]-e.getValue[1]))
.forEach();
负值创建代码建议的相反顺序。要做到这一点,您必须编写自己的比较器。我是比较器新手,但我是否应该向它传递一个函数,将两个int相加到int[]中?
HashMap
不保留任何顺序。如果您正试图这样做,那么您将需要一个不同的数据结构。或者你只是想按这个顺序迭代条目?“Sort aHashMap
在术语上已经是一个矛盾。编辑它是为了澄清映射条目的排序。+1表示“你不能用HashMap创建排序元素”,但我必须承认,我看不出有任何理由不将其保留为TreeMap(不需要“resultMap”)我使用resultMap的原因是为了保留原始结构:HashMap、字符串作为键、int[]作为值。我使用PrintWriter写入文件进行检查,但它会打印内存地址,我想打印int[]?这与此无关,但您可以使用Array.toString正确打印数组<代码>map.entrySet().stream().sorted(map.Entry.comparingByValue((v1,v2)->v2[0]+v2[1]-v1[0]-v1[1]).forEach(e->{System.out.println(“Key:+e.getKey()+”Value:“+Arrays.toString(e.getValue());”)@Hesham--Hi我喜欢这个答案。但我认为通过简单的减法实现比较器存在危险。我在下面的回答中提供了我的解释。同意!感谢您指出这一点,我将添加一个免责声明。这可能是简单的印刷错误。但你们是否同意,这不会影响结果,也不是一般的错误?而且,a[0]+a[1]
不是c
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
(v1, v2) -> v2[0] + v2[1] - v1[0] - v1[1]));
Map<String, int[]> result = new LinkedHashMap<>();
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()));
map.entrySet().stream()
.sorted(Comparator.comparing(e -> 0 - e.getValue()[0] - e.getValue[1]))
.forEach(<whatever>);