可以按值的顺序迭代的映射

时间:2012-01-17 14:51:56

标签: java collections map guava apache-commons

我需要一个可以按其值递减顺序迭代的地图。是否有像Apache Commons或Guava这样的标准库提供这种地图?

8 个答案:

答案 0 :(得分:13)

我会用Guava做到这一点:

Ordering<Map.Entry<Key, Value>> entryOrdering = Ordering.from(valueComparator)
  .onResultOf(new Function<Entry<Key, Value>, Value>() {
    public Value apply(Entry<Key, Value> entry) {
      return entry.getValue();
    }
  }).reverse();
// Desired entries in desired order.  Put them in an ImmutableMap in this order.
ImmutableMap.Builder<Key, Value> builder = ImmutableMap.builder();
for (Entry<Key, Value> entry : 
    entryOrdering.sortedCopy(map.entrySet())) {
  builder.put(entry.getKey(), entry.getValue());
}
return builder.build();
// ImmutableMap iterates over the entries in the desired order

答案 1 :(得分:11)

使用番石榴,甚至比@ LoisWasserman的使用方式更清晰 - 使用Ordering结合Functions.forMap

Ordering.natural().reverse().nullsLast().onResultOf(Functions.forMap(map, null))

或者值不是Comparable

Ordering.fromComparator(yourComparator).reverse().nullsLast().onResultOf(Functions.forMap(map, null))

一个例子(第一个选项 - 自然排序):

final Map<String, String> map = ImmutableMap.of(
    "key 1", "value 1",
    "key 2", "value 2",
    "key 3", "another value",
    "key 4", "zero value");

final Ordering<String> naturalReverseValueOrdering =
    Ordering.natural().reverse().nullsLast().onResultOf(Functions.forMap(map, null));

System.out.println(ImmutableSortedMap.copyOf(map, naturalReverseValueOrdering));

输出:

{key 4=zero value, key 2=value 2, key 1=value 1, key 3=another value}

(我在这里使用ImmutableSortedMap,但如果需要可变性,也可以使用TreeMap。)

修改

如果存在相同的值(更确切地说,如果有两个值Comparator.compare(String v1, String v2)返回0),则ImmutableSortedMap会抛出异常。订购一定不能返回,所以你应该首先按值排序地图,如果两个值相等(键不应该相等),则应使用Ordering.compound

按键。
final Map<String, String> map = ImmutableMap.of(
    "key 1", "value 1",
    "key 2", "value 2",
    "key 3", "zero value",
    "key 4", "zero value");

final Ordering<String> reverseValuesAndNaturalKeysOrdering =
    Ordering.natural().reverse().nullsLast().onResultOf(Functions.forMap(map, null)) // natural for values
        .compound(Ordering.natural()); // secondary - natural ordering of keys

System.out.println(ImmutableSortedMap.copyOf(map, reverseValuesAndNaturalKeysOrdering));

打印:

{key 3=zero value, key 4=zero value, key 2=value 2, key 1=value 1}

答案 2 :(得分:1)

通过降序值获取地图的不可变副本的简单方法。如果您想升序,请移除对reverse()的来电。需要Google Guava

private Map<String, String> mapSortedByValues(Map<String, String> theMap) {
    final Ordering<String> ordering =
            Ordering.natural().reverse().nullsLast().onResultOf(Functions.forMap(theMap, null));

    return ImmutableSortedMap.copyOf(theMap, ordering);
}

答案 3 :(得分:0)

我认为Apache Commons Collections的DualTreeBidiMap应该可以通过迭代inverseBidiMap()的返回来实现这一点。

但我不认为这允许重复值 - 正如名称所说,结构只是基于保留两个树,这是唯一有意义的事情,因为地图对地图结构没有意义。

答案 4 :(得分:0)

如何将值也放在TreeSet中?

for(;;) { yourMap.put(key,value); }
SortedSet sortedValues = new TreeSet(yourMap.values());

SortedSet sortedValues = new TreeSet();
for(;;) 
{
yourMap.put(key,value);
sortedValued.add(value);
}

答案 5 :(得分:0)

我会在列表中添加条目并对其进行排序。我不记得任何可以按值排序的地图,只能按键排序。您可以使用Guava中的BiMap,但它需要值唯一性。

示例:

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>() {{
            put("key1", "value1");
            put("key2", "value3");
            put("key3", "value4");
            put("key4", "value2");
        }};

        List<Map.Entry<String, String>> entries = new ArrayList<>(map.entrySet());
        Collections.sort(entries, new Comparator<Map.Entry<String, String>>() {

            @Override
            public int compare(Entry<String, String> o1,
                    Entry<String, String> o2) {
                if (o1.getValue() == null && o2.getValue() == null) return 0;
                if (o1.getValue() == null) return -1; //Nulls last
                return - o1.getValue().compareTo(o2.getValue());
            }
        });

    }

答案 6 :(得分:0)

我认为你必须推出自己的这种地图的实现。幸运的是,它对Guava来说不应该是一个大问题:

public class SortedValueMap<K, V> extends ForwardingMap<K, V> {

  private Map<K, V> delegate = newHashMap();
  private Comparator<V> valueComparator;

  public static <K, V extends Comparable<V>> SortedValueMap<K, V> reverse() {
    return new SortedValueMap<K, V>(Ordering.<V> natural().reverse());
  }

  public static <K, V> SortedValueMap<K, V> create(Comparator<V> valueComparator) {
    return new SortedValueMap<K, V>(valueComparator);
  }

  protected SortedValueMap(Comparator<V> valueComparator) {
    this.valueComparator = checkNotNull(valueComparator);

  }

  @Override
  protected Map<K, V> delegate() {
    return delegate;
  }

  @Override
  public Set<K> keySet() {
    return new StandardKeySet();
  }

  @Override
  public Set<Map.Entry<K, V>> entrySet() {
    TreeSet<Map.Entry<K, V>> result = newTreeSet(new Comparator<Map.Entry<K, V>>() {
      @Override
      public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
        return ComparisonChain.start()
            .compare(o1.getValue(), o2.getValue(), valueComparator)
            .compare(o1.getKey(), o2.getKey(), Ordering.arbitrary())
            .result();
      }

    });
    result.addAll(Collections.unmodifiableMap(delegate).entrySet());
    return result;
  }

  @Override
  public Collection<V> values() {
    return new StandardValues();
  }

  public static void main(String[] args) {
    SortedValueMap<String, String> svm = SortedValueMap.reverse();
    svm.put("foo", "1");
    svm.put("bar", "3");
    svm.put("baz", "2");

    System.out.println(Joiner.on(", ").withKeyValueSeparator("=").join(svm));
    System.out.println(Joiner.on(", ").join(svm.values()));
    System.out.println(Joiner.on(", ").join(svm.keySet()));
  }
}

此实现中不存在快速失败的迭代器;如果需要,请自行添加。另请注意,通过Map.Entry.setValue设置值会导致对排序顺序的破坏,这就是我在条目集中使用unmodifyableMap的原因。

答案 7 :(得分:0)

现在可以使用Java 8 Streams

在一行中完成此操作
map.entrySet().stream()
    .sorted(Comparator.comparing(Map.Entry::getValue))
    .forEach(...);