2015-11-11 45 views
2

我想排序基于价值的地图。的价值由Java8比较返回奇怪的结果

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

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); 
    } 
} 

输出被证明是正确的

[恤,手袋,封闭鞋,行李]

但是,在输入的时候,我从行李更改为 这是给以下输出

[衬衫,,手袋,已关闭鞋类]

在理想情况下应该排序基于在所述地图的值,而不管键值来完成。但如果钥匙在这里改变,不知道它为什么会改变。

回答

6

问题是在这里:

.collect(Collectors.toSet()); 

因为Collectors.toSet()回报HashSet未订购(因为在大多数情况下,我们并不真正需要的套的订单,但其contains方法,HashSet提供的速度)。

使用,如果你想保留插入顺序

.collect(Collectors.toCollection(LinkedHashSet::new)); 

或取决于你想如何使用这个结果也许使用列表,而不是LinkedHashSet

+0

这工作得很好。你们真棒 – user2166328

+1

@ user2166328不客气。 – Pshemo

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 
+0

这工作得很好。你们真棒 – user2166328

+0

@slartidan对我来说,问题是'shuffle'这个词。对我而言,这意味着顺序是随机的,而你最后一块代码表明它不是。更好的方式是'Collectors.toSet'不保留原始顺序。 –

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));提供一个具体的实现,而不是(这将保留插入顺序)。