按值数对Guava Multimap进行排序

Jef*_*son 7 java guava

如果我有一个Guava Multimap,我将如何根据给定密钥的值数对条目进行排序?

例如:

Multimap<String, String> multiMap = ArrayListMultimap.create();
multiMap.put("foo", "1");
multiMap.put("bar", "2");
multiMap.put("bar", "3");
multiMap.put("bar", "99");
Run Code Online (Sandbox Code Playgroud)

鉴于此,当迭代遍历multiMap时,我如何首先获得"bar"条目(因为"bar"有3个值而"foo"只有1个)?

JB *_*zet 15

提取列表中的条目,然后对列表进行排序:

List<Map.Entry<String, String>> entries = new ArrayList<Map.Entry<String, String>>(map.entries());
Collections.sort(entries, new Comparator<Map.Entry<String, String>>() {
    @Override
    public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
        return Ints.compare(map.get(e2.getKey()).size(), map.get(e1.getKey()).size());
    }
});
Run Code Online (Sandbox Code Playgroud)

然后迭代条目.

编辑:

如果你想要的实际上迭代内部map(Entry<String, Collection<String>>)的条目,那么执行以下操作:

List<Map.Entry<String, Collection<String>>> entries = 
    new ArrayList<Map.Entry<String, Collection<String>>>(map.asMap().entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Collection<String>>>() {
    @Override
    public int compare(Map.Entry<String, Collection<String>> e1, 
                       Map.Entry<String, Collection<String>> e2) {
        return Ints.compare(e2.getValue().size(), e1.getValue().size());
    }
});

// and now iterate
for (Map.Entry<String, Collection<String>> entry : entries) {
    System.out.println("Key = " + entry.getKey());
    for (String value : entry.getValue()) {
        System.out.println("    Value = " + value);
    }
}
Run Code Online (Sandbox Code Playgroud)


Eti*_*veu 8

我将使用Multimap的键Multiset条目,按降序频率对它们进行排序(一旦将问题356中描述的功能添加到Guava,这将更容易),并通过迭代排序的键来构建新的Multimap,从原始Multimap获取值:

/**
 * @return a {@link Multimap} whose entries are sorted by descending frequency
 */
public Multimap<String, String> sortedByDescendingFrequency(Multimap<String, String> multimap) {
    // ImmutableMultimap.Builder preserves key/value order
    ImmutableMultimap.Builder<String, String> result = ImmutableMultimap.builder();
    for (Multiset.Entry<String> entry : DESCENDING_COUNT_ORDERING.sortedCopy(multimap.keys().entrySet())) {
        result.putAll(entry.getElement(), multimap.get(entry.getElement()));
    }
    return result.build();
}

/**
 * An {@link Ordering} that orders {@link Multiset.Entry Multiset entries} by ascending count.
 */
private static final Ordering<Multiset.Entry<?>> ASCENDING_COUNT_ORDERING = new Ordering<Multiset.Entry<?>>() {
    @Override
    public int compare(Multiset.Entry<?> left, Multiset.Entry<?> right) {
        return Ints.compare(left.getCount(), right.getCount());
    }
};

/**
 * An {@link Ordering} that orders {@link Multiset.Entry Multiset entries} by descending count.
 */
private static final Ordering<Multiset.Entry<?>> DESCENDING_COUNT_ORDERING = ASCENDING_COUNT_ORDERING.reverse();
Run Code Online (Sandbox Code Playgroud)

编辑:如果某些条目具有相同的频率,这不起作用(请参阅我的评论)

另一种方法,使用基于Multimaps'键Multiset的Ordering和ImmutableMultimap.Builder.orderKeysBy():

/**
 * @return a {@link Multimap} whose entries are sorted by descending frequency
 */
public Multimap<String, String> sortedByDescendingFrequency(Multimap<String, String> multimap) {
    return ImmutableMultimap.<String, String>builder()
            .orderKeysBy(descendingCountOrdering(multimap.keys()))
            .putAll(multimap)
            .build();
}

private static Ordering<String> descendingCountOrdering(final Multiset<String> multiset) {
    return new Ordering<String>() {
        @Override
        public int compare(String left, String right) {
            return Ints.compare(multiset.count(left), multiset.count(right));
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

第二种方法更短,但我不喜欢Ordering有状态的事实(它取决于Multimap的关键字Multiset比较键​​).

  • 谢谢!第一种方法效果很好,正是我想要的.但第二种方法出了点问题(还没有调试它来弄清楚什么......但是我的测试数据我从一个输入Multimap开始,其asMap()的大小为432,但输出Multimap的asMap()大小只有21岁. (3认同)