Карта, которая может быть повторена в порядке значений - PullRequest
16 голосов
/ 17 января 2012

Мне нужна карта, которая может быть повторена в порядке убывания ее значений . Предоставляет ли какая-либо из стандартных библиотек, таких как Apache Commons или Guava, карту такого типа?

Ответы [ 8 ]

13 голосов
/ 17 января 2012

Я бы сделал это с гуавой следующим образом:

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
11 голосов
/ 17 января 2012

С гуавой есть даже более чистый путь, чем у ответа Лоис Вассермана - использование порядка в сочетании с 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 также можно использовать, если требуется изменчивость.)

EDIT :

Если есть идентичные значения (точнее, если есть два значения, для которых 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}
1 голос
/ 24 февраля 2015

Простой способ получить неизменную копию вашей карты, отсортированную по убыванию.Удалите вызов на 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);
}
0 голосов
/ 21 октября 2017

Теперь это можно сделать одной строкой, используя Java 8 Streams :

map.entrySet().stream()
    .sorted(Comparator.comparing(Map.Entry::getValue))
    .forEach(...);
0 голосов
/ 17 января 2012

Я думаю, вам нужно накатить собственную реализацию такой карты.К счастью, это не должно быть большой проблемой с Гуавой:

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 в наборе записей.

0 голосов
/ 17 января 2012

Я бы внес записи в список и отсортировал их. Я не помню ни одной карты, которую можно упорядочить по значениям, только по ключам. Вы можете использовать BiMap из Guava, но это требует уникальности значений.

Пример:

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

    }
0 голосов
/ 17 января 2012

Как насчет размещения значений также в TreeSet?

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

или

SortedSet sortedValues = new TreeSet();
for(;;) 
{
yourMap.put(key,value);
sortedValued.add(value);
}
0 голосов
/ 17 января 2012

Я думаю, что DualTreeBidiMap коллекций Apache Commons должны сделать это возможным, возможно, путем перебора возврата из inverseBidiMap().

Но я не думаю, что это допускает дублирование значений - как следует из названия, структура просто основана на сохранении двух деревьев, что на самом деле является единственной вещью, которая имеет смысл, поскольку значения в карта не имеет значения для структуры карты.

...