【问题标题】:How to get the two highest values in a HashMap<Integer,String>, making a ranking如何获取HashMap<Integer,String>中的两个最高值,进行排名
【发布时间】:2014-12-18 01:20:13
【问题描述】:

我有以下代码:

public class Tester {

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

        map.put(1, "one");
        map.put(2, "twp");
        map.put(2, "two2");

        int highest = Integer.MIN_VALUE;
        String highestString = null;
        int secondHighest = Integer.MIN_VALUE;
        String secondHighestString = null;

        if (highest == Integer.MIN_VALUE){
            highest = Collections.max(map.keySet() );
            highestString = map.get(highest);
            map.remove(highest);
        }
        if (secondHighest == Integer.MIN_VALUE ){
            secondHighest = Collections.max(map.keySet() );
            secondHighestString = map.get(secondHighest);
            map.remove(secondHighest);
        }

        System.out.println(highest + highestString);
        System.out.println(secondHighest + secondHighestString);


    }

}

正如我尝试的那样,我无法返回两个最高值,因为似乎不可能只删除具有相同键的一个值,而且我还尝试交换它们(制作 HashMap 也不是最佳选择)。我应该尝试使用任何其他类型的集合吗?

我也试过了:

TreeSet<Tete> set = new TreeSet<Tete>();

    set.add(new Tete("name1", 1));
    set.add(new Tete("name2",4));
    set.add(new Tete("name3",4));
    set.add(new Tete("name4",12));

    System.out.println(set.size());

假设“Tete”类只携带一个String和一个Integer,那么集合大小只有3,而不是预期的4。如果我打印每个数字,“name3”将不会被打印,所以我不能返回3个最大值,例如,只有“name4”,“name2”和“name1”会出现,但“name3”大于“name1” "

【问题讨论】:

  • Integer.MIN_VALUE 返回整数的最小值。 (即-2147483648)
  • 是的,它也可能是 0,我只是放了最小值,因为 HashMap 映射中的值之一可能是负数
  • 您希望得到doisdois2HashMap 将用dois2 覆盖dois。请注意如何在 set 中检索键,因此只会存储唯一键。
  • 不,我的意思是,它会为您提供可以设置整数的最小值。不是数组中最小的。
  • 使用自定义比较器查看TreeSet 类。创建具有键和值的自定义类,并将其实例添加到 TreeSet。 TreeSet 总是排序的,因此您可以从顶部获取两个元素,它们将是最高的。注意:TreeSet 在 log(N) 时间提供添加、删除和包含操作。

标签: java


【解决方案1】:

任何Map(包括HashMap)只能为任何键存储一个值。因此,在您调用了三个map.put 之后,您的地图中将只有两个元素:[1,"Um"][2,"dois2"]。值 "dois" 将不再存在于地图中。

如果您确实需要为每个键存储多个值,使用 Java 运行时执行此操作的方法是将每个键映射到一个值列表。

HashMap<Integer,ArrayList<String>> map = new HashMap<Integer,ArrayList<String>>();

这确实意味着向地图添加值和删除值的代码更加复杂。添加一个值需要这样的代码:

ArrayList<String> list = map.get(key);
if (list == null) {
    list = new ArrayList<String>();
    map.put(key, list);
}
list.add(newValue);

删除值类似:您需要获取列表,然后从ArrayList 中删除值,并且仅当ArrayList 的大小现在为0 时才从映射中删除键。

替代方案:第三方库中存在“多地图”集合,例如 Apache Commons (javadoc)。或者,正如@Keenle 建议的那样,您可以使用一个集合,其中键是一个包含 both 整数和字符串的对象。你必须为这个对象编写类,但这很简单。

【讨论】:

  • 我真正需要的是相同值的两个键,而不是,我错误地表达了自己。例如,如果我有一个 HashMap,我需要获取地图中整数最高的字符串。但是,例如,如果我有两个值为 5 的整数,我只能返回一个字符串,而不是两个。
  • @Kerooker 所以在你原来的例子中,你想返回"Um""dois"(或"dois2"),并将它们从地图中删除,但留下"dois2"(或@987654336) @) 在地图上?还是您希望它返回"dois""dois2" 并在地图中留下"Um"?我不太清楚你需要什么。
【解决方案2】:

为了解决排名问题,我会编写一个自定义类,如下所示:

public class TopsCollection<D> {

    private final TreeMap<Integer, List<D>> map 
        = new TreeMap<>((lhv, rhv) -> rhv.compareTo(lhv));

    public TopsCollection() {}

    public void add(Integer score, D name) {
        List<D> vals = map.get(score);
        if (vals == null) {
            vals = new ArrayList<>();
            map.put(score, vals);
        }
        vals.add(name);
    }

    public List<D> getTops(int n) {
        return map.
            values().
            stream().
            limit(n).
            reduce(new ArrayList<D>(), (lhv, rhv) -> {
                lhv.addAll(rhv);
                return lhv;
            });
    }
}

用法:

TopsCollection<String> tc = new TopsCollection<>();
tc.add(12, "nome4");
tc.add(1, "nome1");
tc.add(4, "nome3");
tc.add(4, "nome2");

List<String> tops = tc.getTops(2); // contains 3 elements: nome4, nome3, nome2

备注:

这个特定的实现可以返回任意数量的最高等级相关条目。

还应注意add(...) 方法所花费的时间与log(n) 成正比,因为TopsCollection 由TreeMap 类支持。

如果需要TopCollection 可以实现Collection&lt;T&gt; 接口,使其作为一个真正的集合工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    相关资源
    最近更新 更多