Карта, которая может быть итерирована в порядке значений
Мне нужна карта, которую можно было бы повторить в уменьшающемся порядке ее значений. Использует ли какая-либо из стандартных библиотек, таких как Apache Commons или Guava, такую карту?
Ответы
Ответ 1
Я бы сделал это с 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
Ответ 2
С guava существует еще более чистый способ, чем @LoisWasserman anwer - используя 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}
Ответ 3
Простой метод получения неизменяемой копии вашей карты, отсортированной по убыванию. Удалите вызов 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);
}
Ответ 4
Я думаю, что DualTreeBidiMap
сборников Apache Commons должен сделать это возможным, возможно, путем повторения возврата из inverseBidiMap()
.
Но я не думаю, что это допускает повторяющиеся значения. Как сказано в названии, структура просто основана на сохранении двух деревьев, что действительно единственное, что имеет смысл, поскольку значения на карте не имеют никакого значения для структуры карты.
Ответ 5
Как добавить значения в TreeSet?
for(;;) { yourMap.put(key,value); }
SortedSet sortedValues = new TreeSet(yourMap.values());
или
SortedSet sortedValues = new TreeSet();
for(;;)
{
yourMap.put(key,value);
sortedValued.add(value);
}
Ответ 6
Я бы поместил записи в список и отсортировал его. Я не помню любую карту, которую можно упорядочить по значениям, только клавишами. Вы можете использовать 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());
}
});
}
Ответ 7
Думаю, вам нужно перевернуть свою собственную реализацию такой карты. К счастью, это не должно быть большой проблемой для 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
в наборе записей.
Ответ 8
Теперь это можно сделать в одной строке, используя Java 8 Streams:
map.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getValue))
.forEach(...);