在Java8中比较By Value返回奇怪的结果

时间:2015-11-11 21:32:14

标签: java dictionary java-8

我正在尝试根据值对地图进行排序。

但我看到以下程序的一些奇怪的行为

public class CompareByValueMain {

    public static void main(String[] args) {
        Map<String,Integer> itemIDToTimeMap = new HashMap<String,Integer>();

        itemIDToTimeMap.put("Baggage", 3);
        itemIDToTimeMap.put("Handbag", 16);
        itemIDToTimeMap.put("Closed Footwear", 4);
        itemIDToTimeMap.put("Shirt", 25);

        Set<String> itemIDs = itemIDToTimeMap.entrySet().stream()
                .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toSet());

        System.out.println(itemIDs);
    }
}

输出结果是正确的

[衬衫,手提包,封闭式鞋类,行李]

但是当我从行李更改为行李时的输入 它提供以下输出

[衬衫,手提包,手提包,封闭式鞋类]

理想情况下,应根据地图中的值进行排序,而不管键值如何。但是如果在这里更改密钥,不确定为什么会改变。

3 个答案:

答案 0 :(得分:8)

您正在将结果收集到Set。并非所有Set都保证订单。

所以排序工作正常,但之后它存储在一个不打扰字符串顺序的容器中。

排序完成后,使用List<>之类的容器存储数据。这将保证您商品的订单。

public class CompareByValueMain {

    public static void main(String[] args) {
        Map<String,Integer> itemIDToTimeMap = new HashMap<String,Integer>();

        itemIDToTimeMap.put("Bag", 3); // already changed to "Bag" to demonstrate the working code
        itemIDToTimeMap.put("Handbag", 16);
        itemIDToTimeMap.put("Closed Footwear", 4);
        itemIDToTimeMap.put("Shirt", 25);

        List<String> itemIDs = // use List<> instead of Set<>
                itemIDToTimeMap.entrySet().stream()
                .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toList()); // use .toList() instead of .toSet()

        System.out.println(itemIDs);
    }
}

一个证明差异的简单例子:

public static void main(String[] args) {
    System.out.println("List:");
    Stream
        .of("b", "a")
        .collect(Collectors.toList())
        .stream()
        .forEach(System.out::println);

    System.out.println();
    System.out.println("Set:");
    Stream
        .of("b", "a")
        .collect(Collectors.toSet())
        .stream()
        .forEach(System.out::println);
}

输出:

List:
b
a

Set:
a
b

答案 1 :(得分:6)

问题出在这里:

.collect(Collectors.toSet());

因为Collectors.toSet()会返回未被排序的HashSet(因为在大多数情况下,我们并不需要集合中的顺序,而是contains方法的速度{{1}提供)。

如果您想保留广告订单

,请使用HashSet
LinkedHashSet

或者根据你想要如何使用这个结果,可能会改用List吗?

答案 2 :(得分:4)

默认toSet()收集器返回HashSet,但不保留插入顺序。

请参阅8u60中的实现(请注意,这是内部细节):

public static <T> Collector<T, ?, Set<T>> toSet() {
    return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}

您可以使用.collect(Collectors.toCollection(LinkedHashSet::new));来提供特定的实现(这将保留插入顺序)。