私は比較的Javaに慣れていないので、値でMap<Key, Value>
をソートする必要があることに気付くことがよくあります。
値は一意ではないので、私は自分自身でkeySet
をarray
に変換し、その配列を array sort - カスタムコンパレータを使って キーに関連付けられた値でソートすることに気付きます。
もっと簡単な方法はありますか?
これは一般的なバージョンです。
public class MapUtil {
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
List<Entry<K, V>> list = new ArrayList<>(map.entrySet());
list.sort(Entry.comparingByValue());
Map<K, V> result = new LinkedHashMap<>();
for (Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
}
このコードは複数の方法で中断することがあります。 提供されたコードを使用する予定の場合は、その意味を理解するためにコメントも必ず読んでください。たとえば、値はそれらのキーによって取得できなくなります。 (get
は常にnull
を返します。)
それは前述のすべてよりはるかに簡単に思えます。次のようにTreeMapを使います。
public class Testing {
public static void main(String[] args) {
HashMap<String, Double> map = new HashMap<String, Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
map.put("A", 99.5);
map.put("B", 67.4);
map.put("C", 67.4);
map.put("D", 67.3);
System.out.println("unsorted map: " + map);
sorted_map.putAll(map);
System.out.println("results: " + sorted_map);
}
}
class ValueComparator implements Comparator<String> {
Map<String, Double> base;
public ValueComparator(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with
// equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
}
出力:
unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
results: {D=67.3, B=67.4, C=67.4, A=99.5}
Java 8は新しい答えを提供します。エントリをストリームに変換し、Map.Entryのコンパレータコンビネータを使います
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue());
これにより、エントリを値の昇順で並べ替えることができます。値を降順にする場合は、単純にコンパレータを逆にします。
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
値が比較できない場合は、明示的な比較子を渡すことができます。
Stream<Map.Entry<K,V>> sorted =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(comparator));
その後、他のストリーム操作を使ってデータを消費することができます。たとえば、新しい地図のトップ10が欲しい場合は、
Map<K,V> topTen =
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(10)
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
またはSystem.out
に印刷してください。
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.forEach(System.out::println);
3行の1行の回答...
私は使うだろう Googleコレクション グアバ これを行うには - あなたの値がComparable
であれば、あなたは使用することができます
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
これはマップのための関数(オブジェクト)を作成し(それは入力としてキーのいずれかを取り、それぞれの値を返します)、それからそれらに自然な(匹敵する)順序付けを適用します[値]。
比較できない場合は、次のようにして何かをする必要があります。
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
これらはTreeMap(Ordering
がComparator
を拡張したものとして)に適用されるか、または ソート後に/ - LinkedHashMapに適用されます
NB:あなたがTreeMapを使うつもりなら、もし比較== 0なら、その項目はすでにリストにあることを覚えておいてください(あなたが同じものを比較する複数の値を持っているなら起こります) 。これを軽減するために、あなたのキーをコンパレータに追加することができます(あなたのキーと値がComparable
であると仮定して):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= キーによってマップされた値に自然順序付けを適用し、それをキーの自然順序付けと複合します
あなたのキーが0と比較してもこれはまだ機能しないことに注意してください、しかしこれはほとんどのcomparable
項目には十分であるはずです(hashCode
、equals
およびcompareTo
はしばしば同期しているので...)
Ordering.onResultOf() および Functions.forMap() を参照してください。
だから私たちが望むことをするコンパレータを手に入れたので、それから結果を得る必要があります。
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
これで、おそらくこれでうまくいくでしょうが、
TreeMap
で試してはいけません。挿入されたキーに値がない場合、それがputの後まで比較されても意味がありません。ポイント1は私にとってちょっとした決断の手助けになります。グーグルコレクションは信じられないほど怠惰です(これは良いことです:あなたは瞬時にほとんどすべての操作を行うことができます;実際の作業はあなたが結果を使い始めたときに行われます)、そしてこれは全体マップをコピーする必要があります!
でも心配しないでください。このようにソートされた「ライブ」マップを持っていることに夢中になっているのであれば、上記の問題の一方ではなく両方(!)を次のような問題で解決できます。
注:これは2012年6月に大幅に変更されました。以前のコードは機能しませんでした。TreeMap.get()
- > compare()
とcompare()
- > get()
の間に無限ループを作成せずに値をルックアップするには内部HashMapが必要です
import static org.junit.Assert.assertEquals;
import Java.util.HashMap;
import Java.util.Map;
import Java.util.TreeMap;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
//A map for doing lookups on the keys for comparison so we don't get infinite loops
private final Map<K, V> valueMap;
ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
this(partialValueOrdering, new HashMap<K,V>());
}
private ValueComparableMap(Ordering<? super V> partialValueOrdering,
HashMap<K, V> valueMap) {
super(partialValueOrdering //Apply the value ordering
.onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
.compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
this.valueMap = valueMap;
}
public V put(K k, V v) {
if (valueMap.containsKey(k)){
//remove the key in the sorted set before adding the key again
remove(k);
}
valueMap.put(k,v); //To get "real" unsorted values for the comparator
return super.put(k, v); //Put it in value order
}
public static void main(String[] args){
TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
map.put("a", 5);
map.put("b", 1);
map.put("c", 3);
assertEquals("b",map.firstKey());
assertEquals("a",map.lastKey());
map.put("d",0);
assertEquals("d",map.firstKey());
//ensure it's still a map (by overwriting a key, but with a new value)
map.put("d", 2);
assertEquals("b", map.firstKey());
//Ensure multiple values do not clobber keys
map.put("e", 2);
assertEquals(5, map.size());
assertEquals(2, (int) map.get("e"));
assertEquals(2, (int) map.get("d"));
}
}
置くとき、ハッシュマップがコンパレータの値を持っていることを確認してから、ソートのためにTreeSetに書き込みます。しかしその前に、ハッシュマップを調べて、キーが実際には重複していないことを確認します。また、私たちが作成するコンパレータにもキーが含まれているので、重複する値によって重複しないキーが削除されることはありません(==比較のため)。これら2つの項目はvital forです。地図の契約を守ること。そうしたくないと思うのであれば、マップを(Map<V,K>
に)完全に反転させるところです。
コンストラクタは次のように呼び出す必要があります。
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
からhttp://www.programmersheaven.com/download/49349/download.aspx
private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Object>() {
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
}
});
Map<K, V> result = new LinkedHashMap<>();
for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}
Java 8では、 streams api を使用して、かなり冗長度の低い方法で実行できます。
Map<K, V> sortedMap = map.entrySet().stream()
.sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
キーを並べ替えるには、比較器が比較ごとに各値を調べる必要があります。値を各比較ですぐに利用できるようになるので、もっとスケーラブルな解決策はentrySetを直接使用するでしょう(私はこれを数字でバックアップしていません)。
これはそのようなことの一般的なバージョンです:
public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
final int size = map.size();
final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
list.addAll(map.entrySet());
final ValueComparator<V> cmp = new ValueComparator<V>();
Collections.sort(list, cmp);
final List<K> keys = new ArrayList<K>(size);
for (int i = 0; i < size; i++) {
keys.set(i, list.get(i).getKey());
}
return keys;
}
private static final class ValueComparator<V extends Comparable<? super V>>
implements Comparator<Map.Entry<?, V>> {
public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
上記の解決策のためにメモリローテーションを減らす方法があります。最初に作成されたArrayListは、たとえば戻り値として再利用できます。これはいくつかの一般的な警告の抑制を必要とするでしょうが、再利用可能なライブラリコードのためにそれは価値があるかもしれません。また、コンパレータは毎回の呼び出しで再割り当てされる必要はありません。
以下は魅力的ではありませんが、より効率的です。
public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
final int size = map.size();
final List reusedList = new ArrayList(size);
final List<Map.Entry<K, V>> meView = reusedList;
meView.addAll(map.entrySet());
Collections.sort(meView, SINGLE);
final List<K> keyView = reusedList;
for (int i = 0; i < size; i++) {
keyView.set(i, meView.get(i).getKey());
}
return keyView;
}
private static final Comparator SINGLE = new ValueComparator();
最後に、ソートされた情報にアクセスする必要がある場合は(時々ソートするのではなく)、追加のマルチマップを使用できます。詳細が必要な場合はお知らせください。
Commons-collectionsライブラリには、 TreeBidiMap という名前のソリューションが含まれています。または、Google Collections APIをご覧ください。それは TreeMultimap あなたが使うことができる - を持っています。
そして、あなたがこれらのフレームワークを使いたくないのであれば...彼らはソースコードが付属しています。
私は与えられた答えを見ましたが、それらの多くは必要以上に複雑であるか、いくつかのキーが同じ値を持っているときはマップ要素を削除します。
これは私がよりよく合うと思う解決策です:
public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
int compare = map.get(k2).compareTo(map.get(k1));
if (compare == 0) return 1;
else return compare;
}
};
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
マップは最高値から最低値までソートされていることに注意してください。
これをJava 8の新機能で実現するには
import static Java.util.Map.Entry.comparingByValue;
import static Java.util.stream.Collectors.toList;
<K, V> List<Entry<K, V>> sort(Map<K, V> map, Comparator<? super V> comparator) {
return map.entrySet().stream().sorted(comparingByValue(comparator)).collect(toList());
}
エントリは、指定されたコンパレータを使用してそれらの値によって並べ替えられます。あるいは、値が相互に比較可能な場合は、明示的な比較は必要ありません。
<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}
返されたリストは、このメソッドが呼び出された時点の指定されたマップのスナップショットなので、どちらも他方への以降の変更を反映することはありません。マップのライブの反復可能ビューの場合
<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}
返された反復可能オブジェクトは、反復されるたびに指定されたマップの新しいスナップショットを作成するので、同時変更を除き、常にマップの現在の状態を反映します。
私はマップをソートするための絶え間ない必要性はおそらく匂いであることに同意しますが、私は以下のコードが異なるデータ構造を使用せずにそれをする最も簡単な方法であると思います。
public class MapUtilities {
public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
Collections.sort(entries, new ByValue<K, V>());
return entries;
}
private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
}
そして、これは恥ずかしいほど不完全な単体テストです。
public class MapUtilitiesTest extends TestCase {
public void testSorting() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("One", 1);
map.put("Two", 2);
map.put("Three", 3);
List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
assertEquals("First", "One", sorted.get(0).getKey());
assertEquals("Second", "Two", sorted.get(1).getKey());
assertEquals("Third", "Three", sorted.get(2).getKey());
}
}
結果はMap.Entryオブジェクトのソートされたリストになり、そこからキーと値を取得できます。
カスタマイズしたコンパレータを作成して、新しいTreeMapオブジェクトを作成するときに使用します。
class MyComparator implements Comparator<Object> {
Map<String, Integer> map;
public MyComparator(Map<String, Integer> map) {
this.map = map;
}
public int compare(Object o1, Object o2) {
if (map.get(o2) == map.get(o1))
return 1;
else
return ((Integer) map.get(o2)).compareTo((Integer)
map.get(o1));
}
}
あなたのメイン関数で以下のコードを使用してください
Map<String, Integer> lMap = new HashMap<String, Integer>();
lMap.put("A", 35);
lMap.put("B", 75);
lMap.put("C", 50);
lMap.put("D", 50);
MyComparator comparator = new MyComparator(lMap);
Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator);
newMap.putAll(lMap);
System.out.println(newMap);
出力:
{B=75, D=50, C=50, A=35}
次のような一般的なコンパレータを使用してください。
final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {
private Map<K,V> map;
private MapValueComparator() {
super();
}
public MapValueComparator(Map<K,V> map) {
this();
this.map = map;
}
public int compare(K o1, K o2) {
return map.get(o1).compareTo(map.get(o2));
}
}
あなたが等しい2つのアイテムを持っているとき、最も投票された答えはうまくいきません。 TreeMapは等しい値を除外します。
例:未分類のマップ
キー/値:D/67.3 キー/値:A/99.5 キー/値:B/67.4 キー/値:C/67.5 ____。]キー/値:E/99.5
結果
キー/値:A/99.5 キー/値:C/67.5 キー/値:B/67.4 キー/値:D/67.3 ____。]
だからEを省略!
私にとっては、コンパレータが0にならず-1を返さない場合、コンパレータを調整することでうまくいきました。
例では:
クラスValueComparatorはComparator {を実装しています。
マップベース; public ValueComparator(マップベース){ this.base = base; }
public int compare(オブジェクトa、オブジェクトb){
if((Double)base.get(a) < (Double)base.get(b)) { return 1; } else if((Double)base.get(a) == (Double)base.get(b)) { return -1; } else { return -1; }
} }
今それが戻ります:
未分類の地図:
キー/値:D/67.3 キー/値:A/99.5 キー/値:B/67.4 キー/値:C/67.5 ____。]キー/値:E/99.5
結果:
キー/値:A/99.5 キー/値:E/99.5 キー/値:C/67.5 キー/値:B/67.4 ____。]キー/値:D/67.3
aliens(2011年11月22日)への返答として:私はこの解決法を整数IDと名前のマップに使っていますが、考え方は同じですので、上のコードは正しくないかもしれませんテストでそれを書いて、あなたに正しいコードを与えてください、これは上の解決策に基づいて、Map分類のためのコードです:
package nl.iamit.util;
import Java.util.Comparator;
import Java.util.Map;
public class Comparators {
public static class MapIntegerStringComparator implements Comparator {
Map<Integer, String> base;
public MapIntegerStringComparator(Map<Integer, String> base) {
this.base = base;
}
public int compare(Object a, Object b) {
int compare = ((String) base.get(a))
.compareTo((String) base.get(b));
if (compare == 0) {
return -1;
}
return compare;
}
}
}
これがテストクラスです(私がテストしたところです。これはInteger、String Mapに対して機能します。
package test.nl.iamit.util;
import Java.util.HashMap;
import Java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class TestComparators {
@Test
public void testMapIntegerStringComparator(){
HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
unSoretedMap);
TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
//the testdata:
unSoretedMap.put(new Integer(1), "E");
unSoretedMap.put(new Integer(2), "A");
unSoretedMap.put(new Integer(3), "E");
unSoretedMap.put(new Integer(4), "B");
unSoretedMap.put(new Integer(5), "F");
sorted_map.putAll(unSoretedMap);
Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
Object[] currecntKeys=sorted_map.keySet().toArray();
assertArrayEquals(targetKeys,currecntKeys);
}
}
これは、マップのコンパレータのコードです。
public static class MapStringDoubleComparator implements Comparator {
Map<String, Double> base;
public MapStringDoubleComparator(Map<String, Double> base) {
this.base = base;
}
//note if you want decending in stead of ascending, turn around 1 and -1
public int compare(Object a, Object b) {
if ((Double) base.get(a) == (Double) base.get(b)) {
return 0;
} else if((Double) base.get(a) < (Double) base.get(b)) {
return -1;
}else{
return 1;
}
}
}
これがこのテストケースです。
@Test
public void testMapStringDoubleComparator(){
HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
unSoretedMap);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
//the testdata:
unSoretedMap.put("D",new Double(67.3));
unSoretedMap.put("A",new Double(99.5));
unSoretedMap.put("B",new Double(67.4));
unSoretedMap.put("C",new Double(67.5));
unSoretedMap.put("E",new Double(99.5));
sorted_map.putAll(unSoretedMap);
Object[] targetKeys={"D","B","C","E","A"};
Object[] currecntKeys=sorted_map.keySet().toArray();
assertArrayEquals(targetKeys,currecntKeys);
}
あなたはこれをもっともっと一般的にすることができますが、私はちょうど1つのケースのためにそれを必要としました(地図)
Collections.sort
を使用する代わりに、Arrays.sort
を使用することをお勧めします。実際にはCollections.sort
は次のようになります。
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] a = list.toArray();
Arrays.sort(a);
ListIterator<T> i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
リスト上でtoArray
を呼び出してからArrays.sort
を使用するだけです。これにより、すべてのマップエントリが3回コピーされます。1回はマップから一時リスト(LinkedListまたはArrayList)へ、次に一時配列へ、最後に新しいマップへです。
不要なLinkedListが作成されないため、私のソリューションではこの1ステップを省略しています。これは一般的にやさしく、パフォーマンスに最適なコードです。
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map)
{
@SuppressWarnings("unchecked")
Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]);
Arrays.sort(array, new Comparator<Map.Entry<K, V>>()
{
public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2)
{
return e1.getValue().compareTo(e2.getValue());
}
});
Map<K, V> result = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : array)
result.put(entry.getKey(), entry.getValue());
return result;
}
これはAnthonyの答えの変形であり、値が重複しているとうまくいきません。
public static <K, V extends Comparable<V>> Map<K, V> sortMapByValues(final Map<K, V> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
final V v1 = map.get(k1);
final V v2 = map.get(k2);
/* Not sure how to handle nulls ... */
if (v1 == null) {
return (v2 == null) ? 0 : 1;
}
int compare = v2.compareTo(v1);
if (compare != 0)
{
return compare;
}
else
{
Integer h1 = k1.hashCode();
Integer h2 = k2.hashCode();
return h2.compareTo(h1);
}
}
};
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
Nullを処理する方法は、どちらかといえば空中で起きていることに注意してください。
このアプローチの重要な利点の1つは、ここで提供されている他の解決策とは異なり、実際にはMapが返されることです。
大きな問題あなたが最初の答えを使うなら(Googleはあなたをここに連れて行って)、等しい句を追加するようにコンパレータを変更してください。
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return 1;
} else if (base.get(a) < base.get(b)){
return -1;
}
return 0;
// returning 0 would merge keys
}
ベストアプローチ
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import Java.util.Set;
import Java.util.Map.Entry;
public class OrderByValue {
public static void main(String a[]){
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Java", 20);
map.put("C++", 45);
map.put("Unix", 67);
map.put("MAC", 26);
map.put("Why this kolavari", 93);
Set<Entry<String, Integer>> set = map.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
{
public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
{
return (o1.getValue()).compareTo( o2.getValue() );//Ascending order
//return (o2.getValue()).compareTo( o1.getValue() );//Descending order
}
} );
for(Map.Entry<String, Integer> entry:list){
System.out.println(entry.getKey()+" ==== "+entry.getValue());
}
}}
出力
Java ==== 20
MAC ==== 26
C++ ==== 45
Unix ==== 67
Why this kolavari ==== 93
この質問にはすでに多くの答えがありますが、探しているもの、つまりキーとエントリを関連付けられた値でソートして返し、キーと値がマップ内で変更されるときにこのプロパティを維持するマップの実装はありません。 2つの その他質問 具体的にこれをお願いします。
このユースケースを解決する一般的なわかりやすい例を作り上げました。この実装は、値の変更を反映したり、元のオブジェクトのkeySet()およびentrySet()から返されたセットに削除を反映するなど、Mapインタフェースのすべての規約を尊重しません。私はそのような解決策はスタックオーバーフローの答えに含めるには大きすぎるだろうと感じました。もっと完全な実装を作れたなら、おそらくそれをGithubに投稿してから、この回答の最新版にリンクします。
import Java.util.*;
/**
* A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
* by associated values based on the the comparator provided at construction
* time. The order of two or more keys with identical values is not defined.
* <p>
* Several contracts of the Map interface are not satisfied by this minimal
* implementation.
*/
public class ValueSortedMap<K, V> extends HashMap<K, V> {
protected Map<V, Collection<K>> valueToKeysMap;
// uses natural order of value object, if any
public ValueSortedMap() {
this((Comparator<? super V>) null);
}
public ValueSortedMap(Comparator<? super V> valueComparator) {
this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
}
public boolean containsValue(Object o) {
return valueToKeysMap.containsKey(o);
}
public V put(K k, V v) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
valueToKeysMap.get(oldV).remove(k);
}
super.put(k, v);
if (!valueToKeysMap.containsKey(v)) {
Collection<K> keys = new ArrayList<K>();
keys.add(k);
valueToKeysMap.put(v, keys);
} else {
valueToKeysMap.get(v).add(k);
}
return oldV;
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public V remove(Object k) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
super.remove(k);
valueToKeysMap.get(oldV).remove(k);
}
return oldV;
}
public void clear() {
super.clear();
valueToKeysMap.clear();
}
public Set<K> keySet() {
LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
for (V v : valueToKeysMap.keySet()) {
Collection<K> keys = valueToKeysMap.get(v);
ret.addAll(keys);
}
return ret;
}
public Set<Map.Entry<K, V>> entrySet() {
LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
for (Collection<K> keys : valueToKeysMap.values()) {
for (final K k : keys) {
final V v = get(k);
ret.add(new Map.Entry<K,V>() {
public K getKey() {
return k;
}
public V getValue() {
return v;
}
public V setValue(V v) {
throw new UnsupportedOperationException();
}
});
}
}
return ret;
}
}
状況に応じて、Java.util.LinkedHashMap<T>
を使用すると、アイテムがマップに配置される順序が記憶されます。そうでなければ、もしあなたがそれらの自然な順序に基づいて値をソートする必要があるなら、私はCollections.sort()
によってソートされることができる別のリストを維持することをお勧めします。
TreeMap <>は に等しい値には機能しないため、これを使用しました。
private <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
return list;
}
list を LinkedHashMap の中に入れたいと思うかもしれませんが、ただそれをただ反復しようとするのであれば、それは不要です...
これは複雑すぎます。地図はそれらを値で分類するような仕事をすることになっていませんでした。最も簡単な方法はあなたの要求に合うようにあなた自身のクラスを作成することです。
下の例では、TreeMapに*がある場所にコンパレータを追加することになっています。しかしJava APIでは、コンパレータには値ではなくキーのみを渡します。ここに記載されている例はすべて2つのマップに基づいています。 1つのハッシュと1つの新しいツリーこれは奇妙です。
例:
Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*);
それで、このように地図をセットに変えてください:
ResultComparator rc = new ResultComparator();
Set<Results> set = new TreeSet<Results>(rc);
クラスResults
を作成します。
public class Results {
private Driver driver;
private Float time;
public Results(Driver driver, Float time) {
this.driver = driver;
this.time = time;
}
public Float getTime() {
return time;
}
public void setTime(Float time) {
this.time = time;
}
public Driver getDriver() {
return driver;
}
public void setDriver (Driver driver) {
this.driver = driver;
}
}
そしてComparatorクラス:
public class ResultsComparator implements Comparator<Results> {
public int compare(Results t, Results t1) {
if (t.getTime() < t1.getTime()) {
return 1;
} else if (t.getTime() == t1.getTime()) {
return 0;
} else {
return -1;
}
}
}
これにより、依存関係を簡単に追加できます。
そして最後に、簡単なイテレータを追加します。
Iterator it = set.iterator();
while (it.hasNext()) {
Results r = (Results)it.next();
System.out.println( r.getDriver().toString
//or whatever that is related to Driver class -getName() getSurname()
+ " "
+ r.getTime()
);
}
最も明確な方法は、値を使ってマップをソートするためにコレクションを利用することです。
Map<String, Long> map = new HashMap<String, Long>();
// populate with data to sort on Value
// use datastructure designed for sorting
Queue queue = new PriorityQueue( map.size(), new MapComparable() );
queue.addAll( map.entrySet() );
// get a sorted map
LinkedHashMap<String, Long> linkedMap = new LinkedHashMap<String, Long>();
for (Map.Entry<String, Long> entry; (entry = queue.poll())!=null;) {
linkedMap.put(entry.getKey(), entry.getValue());
}
public static class MapComparable implements Comparator<Map.Entry<String, Long>>{
public int compare(Entry<String, Long> e1, Entry<String, Long> e2) {
return e1.getValue().compareTo(e2.getValue());
}
}
これはOO解決策です(すなわち、static
メソッドを使用しません)。
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.HashMap;
import Java.util.Iterator;
import Java.util.LinkedList;
import Java.util.LinkedHashMap;
import Java.util.List;
import Java.util.Map;
public class SortableValueMap<K, V extends Comparable<V>>
extends LinkedHashMap<K, V> {
public SortableValueMap() { }
public SortableValueMap( Map<K, V> map ) {
super( map );
}
public void sortByValue() {
List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( entrySet() );
Collections.sort( list, new Comparator<Map.Entry<K, V>>() {
public int compare( Map.Entry<K, V> entry1, Map.Entry<K, V> entry2 ) {
return entry1.getValue().compareTo( entry2.getValue() );
}
});
clear();
for( Map.Entry<K, V> entry : list ) {
put( entry.getKey(), entry.getValue() );
}
}
private static void print( String text, Map<String, Double> map ) {
System.out.println( text );
for( String key : map.keySet() ) {
System.out.println( "key/value: " + key + "/" + map.get( key ) );
}
}
public static void main( String[] args ) {
SortableValueMap<String, Double> map =
new SortableValueMap<String, Double>();
map.put( "A", 67.5 );
map.put( "B", 99.5 );
map.put( "C", 82.4 );
map.put( "D", 42.0 );
print( "Unsorted map", map );
map.sortByValue();
print( "Sorted map", map );
}
}
これによりパブリックドメインに寄付されました。
重複値を持つペアを持つソートマップを作成するための簡単な変更。 compareメソッド(ValueComparatorクラス)では、値が等しい場合は0を返さず、2つのキーの比較結果を返します。キーはマップ内で区別できるので、重複した値を保持することに成功します(これはキーによってソートされます)。したがって、上記の例は次のように変更できます。
public int compare(Object a, Object b) {
if((Double)base.get(a) < (Double)base.get(b)) {
return 1;
} else if((Double)base.get(a) == (Double)base.get(b)) {
return ((String)a).compareTo((String)b);
} else {
return -1;
}
}
}
@devinmooreコードに基づいて、総称を使用し、昇順と降順の両方をサポートするマップソート方法.
/**
* Sort a map by it's keys in ascending order.
*
* @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
* @author Maxim Veksler
*/
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map) {
return sortMapByKey(map, SortingOrder.ASCENDING);
}
/**
* Sort a map by it's values in ascending order.
*
* @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
* @author Maxim Veksler
*/
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map) {
return sortMapByValue(map, SortingOrder.ASCENDING);
}
/**
* Sort a map by it's keys.
*
* @param sortingOrder {@link SortingOrder} enum specifying requested sorting order.
* @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
* @author Maxim Veksler
*/
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map, final SortingOrder sortingOrder) {
Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return comparableCompare(o1.getKey(), o2.getKey(), sortingOrder);
}
};
return sortMap(map, comparator);
}
/**
* Sort a map by it's values.
*
* @param sortingOrder {@link SortingOrder} enum specifying requested sorting order.
* @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
* @author Maxim Veksler
*/
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map, final SortingOrder sortingOrder) {
Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return comparableCompare(o1.getValue(), o2.getValue(), sortingOrder);
}
};
return sortMap(map, comparator);
}
@SuppressWarnings("unchecked")
private static <T> int comparableCompare(T o1, T o2, SortingOrder sortingOrder) {
int compare = ((Comparable<T>)o1).compareTo(o2);
switch (sortingOrder) {
case ASCENDING:
return compare;
case DESCENDING:
return (-1) * compare;
}
return 0;
}
/**
* Sort a map by supplied comparator logic.
*
* @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
* @author Maxim Veksler
*/
public static <K, V> LinkedHashMap<K, V> sortMap(final Map<K, V> map, final Comparator<Map.Entry<K, V>> comparator) {
// Convert the map into a list of key,value pairs.
List<Map.Entry<K, V>> mapEntries = new LinkedList<Map.Entry<K, V>>(map.entrySet());
// Sort the converted list according to supplied comparator.
Collections.sort(mapEntries, comparator);
// Build a new ordered map, containing the same entries as the old map.
LinkedHashMap<K, V> result = new LinkedHashMap<K, V>(map.size() + (map.size() / 20));
for(Map.Entry<K, V> entry : mapEntries) {
// We iterate on the mapEntries list which is sorted by the comparator putting new entries into
// the targeted result which is a sorted map.
result.put(entry.getKey(), entry.getValue());
}
return result;
}
/**
* Sorting order enum, specifying request result sort behavior.
* @author Maxim Veksler
*
*/
public static enum SortingOrder {
/**
* Resulting sort will be from smaller to biggest.
*/
ASCENDING,
/**
* Resulting sort will be from biggest to smallest.
*/
DESCENDING
}
確かにスティーブンの解決策は本当に素晴らしいですが、グアバを使用することができない人のために:
これは私がマップを値でソートするための解決策です。
// If you want to sort a map by value, and if there can be twice the same value:
// here is your original map
Map<String,Integer> mapToSortByValue = new HashMap<String, Integer>();
mapToSortByValue.put("A", 3);
mapToSortByValue.put("B", 1);
mapToSortByValue.put("C", 3);
mapToSortByValue.put("D", 5);
mapToSortByValue.put("E", -1);
mapToSortByValue.put("F", 1000);
mapToSortByValue.put("G", 79);
mapToSortByValue.put("H", 15);
// Sort all the map entries by value
Set<Map.Entry<String,Integer>> set = new TreeSet<Map.Entry<String,Integer>>(
new Comparator<Map.Entry<String,Integer>>(){
@Override
public int compare(Map.Entry<String,Integer> obj1, Map.Entry<String,Integer> obj2) {
Integer val1 = obj1.getValue();
Integer val2 = obj2.getValue();
// DUPLICATE VALUE CASE
// If the values are equals, we can't return 0 because the 2 entries would be considered
// as equals and one of them would be deleted (because we use a set, no duplicate, remember!)
int compareValues = val1.compareTo(val2);
if ( compareValues == 0 ) {
String key1 = obj1.getKey();
String key2 = obj2.getKey();
int compareKeys = key1.compareTo(key2);
if ( compareKeys == 0 ) {
// what you return here will tell us if you keep REAL KEY-VALUE duplicates in your set
// if you want to, do whatever you want but do not return 0 (but don't break the comparator contract!)
return 0;
}
return compareKeys;
}
return compareValues;
}
}
);
set.addAll(mapToSortByValue.entrySet());
// OK NOW OUR SET IS SORTED COOL!!!!
// And there's nothing more to do: the entries are sorted by value!
for ( Map.Entry<String,Integer> entry : set ) {
System.out.println("Set entries: " + entry.getKey() + " -> " + entry.getValue());
}
// But if you add them to an hashmap
Map<String,Integer> myMap = new HashMap<String,Integer>();
// When iterating over the set the order is still good in the println...
for ( Map.Entry<String,Integer> entry : set ) {
System.out.println("Added to result map entries: " + entry.getKey() + " " + entry.getValue());
myMap.put(entry.getKey(), entry.getValue());
}
// But once they are in the hashmap, the order is not kept!
for ( Integer value : myMap.values() ) {
System.out.println("Result map values: " + value);
}
// Also this way doesn't work:
// Logic because the entryset is a hashset for hashmaps and not a treeset
// (and even if it was a treeset, it would be on the keys only)
for ( Map.Entry<String,Integer> entry : myMap.entrySet() ) {
System.out.println("Result map entries: " + entry.getKey() + " -> " + entry.getValue());
}
// CONCLUSION:
// If you want to iterate on a map ordered by value, you need to remember:
// 1) Maps are only sorted by keys, so you can't sort them directly by value
// 2) So you simply CAN'T return a map to a sortMapByValue function
// 3) You can't reverse the keys and the values because you have duplicate values
// This also means you can't neither use Guava/Commons bidirectionnal treemaps or stuff like that
// SOLUTIONS
// So you can:
// 1) only sort the values which is easy, but you loose the key/value link (since you have duplicate values)
// 2) sort the map entries, but don't forget to handle the duplicate value case (like i did)
// 3) if you really need to return a map, use a LinkedHashMap which keep the insertion order
エグゼクティブ: http://www.ideone.com/dq3Lu
出力:
Set entries: E -> -1
Set entries: B -> 1
Set entries: A -> 3
Set entries: C -> 3
Set entries: D -> 5
Set entries: H -> 15
Set entries: G -> 79
Set entries: F -> 1000
Added to result map entries: E -1
Added to result map entries: B 1
Added to result map entries: A 3
Added to result map entries: C 3
Added to result map entries: D 5
Added to result map entries: H 15
Added to result map entries: G 79
Added to result map entries: F 1000
Result map values: 5
Result map values: -1
Result map values: 1000
Result map values: 79
Result map values: 3
Result map values: 1
Result map values: 3
Result map values: 15
Result map entries: D -> 5
Result map entries: E -> -1
Result map entries: F -> 1000
Result map entries: G -> 79
Result map entries: A -> 3
Result map entries: B -> 1
Result map entries: C -> 3
Result map entries: H -> 15
それが何人かの人々を助けることを願っています
Guavaのマルチマップを試すことができます。
TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
Multimaps.invertFrom(Multimaps.forMap(originalMap),
ArrayListMultimap.<Integer, String>create()).asMap());
その結果、元の値からそれらに対応するキーのコレクションへのマップが得られます。この方法は、同じ値に対して複数のキーがある場合でも使用できます。
レイトエントリー.
Java-8の出現により、ストリームをデータ操作に非常に簡単/簡潔な方法で使用できます。ストリームを使用してマップエントリを値で並べ替え、 LinkedHashMap を作成して挿入順序反復を保持することができます。
例えば:
LinkedHashMap sortedByValueMap = map.entrySet().stream()
.sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)) //first sorting by Value, then sorting by Key(entries with same value)
.collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);
逆の順序で交換する場合:
comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
と
comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()
User157196とCarter Pageのソリューションを統合しました。
class MapUtil {
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue( Map<K, V> map ){
ValueComparator<K,V> bvc = new ValueComparator<K,V>(map);
TreeMap<K,V> sorted_map = new TreeMap<K,V>(bvc);
sorted_map.putAll(map);
return sorted_map;
}
}
class ValueComparator<K, V extends Comparable<? super V>> implements Comparator<K> {
Map<K, V> base;
public ValueComparator(Map<K, V> base) {
this.base = base;
}
public int compare(K a, K b) {
int result = (base.get(a).compareTo(base.get(b)));
if (result == 0) result=1;
// returning 0 would merge keys
return result;
}
}
重複したキーがあり、少数のデータ(<1000)だけがあり、コードがパフォーマンス上重要ではない場合は、次のようにします。
Map<String,Integer> tempMap=new HashMap<String,Integer>(inputUnsortedMap);
LinkedHashMap<String,Integer> sortedOutputMap=new LinkedHashMap<String,Integer>();
for(int i=0;i<inputUnsortedMap.size();i++){
Map.Entry<String,Integer> maxEntry=null;
Integer maxValue=-1;
for(Map.Entry<String,Integer> entry:tempMap.entrySet()){
if(entry.getValue()>maxValue){
maxValue=entry.getValue();
maxEntry=entry;
}
}
tempMap.remove(maxEntry.getKey());
sortedOutputMap.put(maxEntry.getKey(),maxEntry.getValue());
}
inputUnsortedMap はコードへの入力です。
変数 sortedOutputMap には、繰り返したときに降順でデータが含まれます。順序を変更するには、if文の>を<に変更します。
最速のソートではありませんが、追加の依存関係なしにジョブを実行します。
私がこれに直面したとき、私はただ側面にリストを作成します。カスタムのMap実装にそれらをまとめると、いい感じになるでしょう。次のようなものを使用して、必要なときだけソートを実行することができます。 (注:私は実際にはこれをテストしていませんが、コンパイルしています...どこかに愚かな小さなバグがあるかもしれません)
(キーと値の両方でソートしたい場合は、TreeMapクラスを拡張し、アクセサメソッドを定義しないで、ミューテータにmap_.xxxxの代わりにsuper.xxxxxを呼び出させる)
package com.javadude.sample;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import Java.util.Set;
public class SortedValueHashMap<K, V> implements Map<K, V> {
private Map<K, V> map_ = new HashMap<K, V>();
private List<V> valueList_ = new ArrayList<V>();
private boolean needsSort_ = false;
private Comparator<V> comparator_;
public SortedValueHashMap() {
}
public SortedValueHashMap(List<V> valueList) {
valueList_ = valueList;
}
public List<V> sortedValues() {
if (needsSort_) {
needsSort_ = false;
Collections.sort(valueList_, comparator_);
}
return valueList_;
}
// mutators
public void clear() {
map_.clear();
valueList_.clear();
needsSort_ = false;
}
public V put(K key, V value) {
valueList_.add(value);
needsSort_ = true;
return map_.put(key, value);
}
public void putAll(Map<? extends K, ? extends V> m) {
map_.putAll(m);
valueList_.addAll(m.values());
needsSort_ = true;
}
public V remove(Object key) {
V value = map_.remove(key);
valueList_.remove(value);
return value;
}
// accessors
public boolean containsKey(Object key) { return map_.containsKey(key); }
public boolean containsValue(Object value) { return map_.containsValue(value); }
public Set<Java.util.Map.Entry<K, V>> entrySet() { return map_.entrySet(); }
public boolean equals(Object o) { return map_.equals(o); }
public V get(Object key) { return map_.get(key); }
public int hashCode() { return map_.hashCode(); }
public boolean isEmpty() { return map_.isEmpty(); }
public Set<K> keySet() { return map_.keySet(); }
public int size() { return map_.size(); }
public Collection<V> values() { return map_.values(); }
}
このメソッドは目的を果たすだけです。 ( 'setback'はValues がJava.util.Comparableインターフェース を実装しなければならないということです)
/**
* Sort a map according to values.
* @param <K> the key of the map.
* @param <V> the value to sort according to.
* @param mapToSort the map to sort.
* @return a map sorted on the values.
*/
public static <K, V extends Comparable< ? super V>> Map<K, V>
sortMapByValues(final Map <K, V> mapToSort)
{
List<Map.Entry<K, V>> entries =
new ArrayList<Map.Entry<K, V>>(mapToSort.size());
entries.addAll(mapToSort.entrySet());
Collections.sort(entries,
new Comparator<Map.Entry<K, V>>()
{
@Override
public int compare(
final Map.Entry<K, V> entry1,
final Map.Entry<K, V> entry2)
{
return entry1.getValue().compareTo(entry2.getValue());
}
});
Map<K, V> sortedMap = new LinkedHashMap<K, V>();
for (Map.Entry<K, V> entry : entries)
{
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
http://javawithswaranga.blogspot.com/2011/06/generic-method-to-sort-hashmap.html
これは AbacusUtil 付きのJava 8によるコードです。
Map<String, Integer> map = N.asMap("a", 2, "b", 3, "c", 1, "d", 2);
Map<String, Integer> sortedMap = Stream.of(map.entrySet()).sorted(Map.Entry.comparingByValue()).toMap(e -> e.getKey(), e -> e.getValue(),
LinkedHashMap::new);
N.println(sortedMap);
// output: {c=1, a=2, d=2, b=3}
宣言:私はAbacusUtilの開発者です。
私の解決策は、ほとんどのAPIを使用するという点で非常に単純なアプローチです。 Mapの機能を使用して、コンテンツをSetとしてentrySet()メソッド経由でエクスポートします。これでSetを含むMap.Entryオブジェクトができました。
さて、セットには注文はありませんが、コンテンツを取得してArrayListに入れることができます。現在はrandomの順序になっていますが、とにかく並べ替えます。
ArrayListはCollectionであるため、ここでCollections.sort()メソッドを使用して、秩序を混乱に陥れます。 Map.Entryオブジェクトは必要な比較の種類を認識しないため、カスタムComparatorを提供します。
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("Z", "E");
map.put("G", "A");
map.put("D", "C");
map.put("E", null);
map.put("O", "C");
map.put("L", "D");
map.put("Q", "B");
map.put("A", "F");
map.put(null, "X");
MapEntryComparator mapEntryComparator = new MapEntryComparator();
List<Entry<String,String>> entryList = new ArrayList<>(map.entrySet());
Collections.sort(entryList, mapEntryComparator);
for (Entry<String, String> entry : entryList) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
回答の私のバージョンを投稿する
List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, (obj1, obj2) -> obj2.getValue().compareTo(obj1.getValue()));
Map<String, Integer> resultMap = new LinkedHashMap<>();
list.forEach(arg0 -> {
resultMap.put(arg0.getKey(), arg0.getValue());
});
System.out.println(resultMap);
HashMap<String, Long>
のための最も簡単な brute-force sortHashMap
メソッド: コピーペーストして を使うことができます
public class Test {
public static void main(String[] args) {
HashMap<String, Long> hashMap = new HashMap<>();
hashMap.put("Cat", (long) 4);
hashMap.put("Human", (long) 2);
hashMap.put("Dog", (long) 4);
hashMap.put("Fish", (long) 0);
hashMap.put("Tree", (long) 1);
hashMap.put("Three-legged-human", (long) 3);
hashMap.put("Monkey", (long) 2);
System.out.println(hashMap); //{Human=2, Cat=4, Three-legged-human=3, Monkey=2, Fish=0, Tree=1, Dog=4}
System.out.println(sortHashMap(hashMap)); //{Cat=4, Dog=4, Three-legged-human=3, Human=2, Monkey=2, Tree=1, Fish=0}
}
public LinkedHashMap<String, Long> sortHashMap(HashMap<String, Long> unsortedMap) {
LinkedHashMap<String, Long> result = new LinkedHashMap<>();
//add String keys to an array: the array would get sorted, based on those keys' values
ArrayList<String> sortedKeys = new ArrayList<>();
for (String key: unsortedMap.keySet()) {
sortedKeys.add(key);
}
//sort the ArrayList<String> of keys
for (int i=0; i<unsortedMap.size(); i++) {
for (int j=1; j<sortedKeys.size(); j++) {
if (unsortedMap.get(sortedKeys.get(j)) > unsortedMap.get(sortedKeys.get(j-1))) {
String temp = sortedKeys.get(j);
sortedKeys.set(j, sortedKeys.get(j-1));
sortedKeys.set(j-1, temp);
}
}
}
// construct the result Map
for (String key: sortedKeys) {
result.put(key, unsortedMap.get(key));
}
return result;
}
}
Iteratorを使わずにマップをその値でソートするdevinmooreの方法を書き直しました。
public static Map<K, V> sortMapByValue(Map<K, V> inputMap) {
Set<Entry<K, V>> set = inputMap.entrySet();
List<Entry<K, V>> list = new ArrayList<Entry<K, V>>(set);
Collections.sort(list, new Comparator<Map.Entry<K, V>>()
{
@Override
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return (o1.getValue()).compareTo( o2.getValue() ); //Ascending order
}
} );
Map<K, V> sortedMap = new LinkedHashMap<>();
for(Map.Entry<K, V> entry : list){
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
出力マップとしてLinkedHashMap
を使用したNote:
。リストは値でソートされているので、挿入されたキーの順序で値を出力マップに格納する必要があります。たとえば、出力マップとしてTreeMap
を使用すると、マップは再びマップキーでソートされます。
これが主な方法です。
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("3", "three");
map.put("1", "one");
map.put("5", "five");
System.out.println("Input Map:" + map);
System.out.println("Sorted Map:" + sortMapByValue(map));
}
最後に、これは出力です:
Input Map:{1=one, 3=three, 5=five}
Sorted Map:{5=five, 1=one, 3=three}
Guavaライブラリの使用:
public static <K,V extends Comparable<V>>SortedMap<K,V> sortByValue(Map<K,V> original){
var comparator = Ordering.natural()
.reverse() // highest first
.nullsLast()
.onResultOf(Functions.forMap(original, null))
.compound(Ordering.usingToString());
return ImmutableSortedMap.copyOf(original, comparator);
}
public class Test {
public static void main(String[] args) {
TreeMap<Integer, String> hm=new TreeMap();
hm.put(3, "arun singh");
hm.put(5, "vinay singh");
hm.put(1, "bandagi singh");
hm.put(6, "vikram singh");
hm.put(2, "panipat singh");
hm.put(28, "jakarta singh");
ArrayList<String> al=new ArrayList(hm.values());
Collections.sort(al, new myComparator());
System.out.println("//sort by values \n");
for(String obj: al){
for(Map.Entry<Integer, String> map2:hm.entrySet()){
if(map2.getValue().equals(obj)){
System.out.println(map2.getKey()+" "+map2.getValue());
}
}
}
}
}
class myComparator implements Comparator{
@Override
public int compare(Object o1, Object o2) {
String o3=(String) o1;
String o4 =(String) o2;
return o3.compareTo(o4);
}
}
OUTPUT =
//sort by values
3 arun singh
1 bandagi singh
28 jakarta singh
2 panipat singh
6 vikram singh
5 vinay singh
ソートメソッドを起動したりユーティリティに明示的に渡したりせずに、値で本質的にソートするMap
データ構造を使用することが望ましい場合は、次の解決策が適用可能です。
(1) org.drools.chance.core.util.ValueSortedMap (JBossプロジェクト)は内部的にルックアップ用とソートされた値を保持するための2つのマップを保持します。以前に追加された答えと非常に似ていますが、おそらくそれは外部からの使用をより安全にするための抽象化とカプセル化の部分(コピーメカニズムを含む)です。
(2) http://techblog.molindo.at/2008/11/Java-map-sorted-by-value.html は2つのマップを維持するのを避け、代わりにApache CommonのLinkedMapを信頼します。 (ブログ作成者のメモ:as all the code here is in the public domain
):
// required to access LinkEntry.before and LinkEntry.after
package org.Apache.commons.collections.map;
// SNIP: imports
/**
* map implementation based on LinkedMap that maintains a sorted list of
* values for iteration
*/
public class ValueSortedHashMap extends LinkedMap {
private final boolean _asc;
// don't use super()!
public ValueSortedHashMap(final boolean asc) {
super(DEFAULT_CAPACITY);
_asc = asc;
}
// SNIP: some more constructors with initial capacity and the like
protected void addEntry(final HashEntry entry, final int hashIndex) {
final LinkEntry link = (LinkEntry) entry;
insertSorted(link);
data[hashIndex] = entry;
}
protected void updateEntry(final HashEntry entry, final Object newValue) {
entry.setValue(newValue);
final LinkEntry link = (LinkEntry) entry;
link.before.after = link.after;
link.after.before = link.before;
link.after = link.before = null;
insertSorted(link);
}
private void insertSorted(final LinkEntry link) {
LinkEntry cur = header;
// iterate whole list, could (should?) be replaced with quicksearch
// start at end to optimize speed for in-order insertions
while ((cur = cur.before) != header & amp; & amp; !insertAfter(cur, link)) {}
link.after = cur.after;
link.before = cur;
cur.after.before = link;
cur.after = link;
}
protected boolean insertAfter(final LinkEntry cur, final LinkEntry link) {
if (_asc) {
return ((Comparable) cur.getValue())
.compareTo((V) link.getValue()) & lt; = 0;
} else {
return ((Comparable) cur.getValue())
.compareTo((V) link.getValue()) & gt; = 0;
}
}
public boolean isAscending() {
return _asc;
}
}
(3)必要に応じて列挙中にのみソートされるカスタムのMap
またはLinkedHashMap
から拡張したものを記述します(例:values()
、keyset()
、entryset()
)。内部の実装/動作はこのクラスを使用しているものから抽象化されていますが、列挙の要求時に値が常にソートされるようにこのクラスのクライアントには見えます。このクラスは、列挙の前にすべてのput
操作が完了している場合にソートがほぼ1回行われることを期待しています。ソート方法は、この質問に対する以前の回答の一部を採用しています。
public class SortByValueMap<K, V> implements Map<K, V> {
private boolean isSortingNeeded = false;
private final Map<K, V> map = new LinkedHashMap<>();
@Override
public V put(K key, V value) {
isSortingNeeded = true;
return map.put(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
isSortingNeeded = true;
map.putAll(map);
}
@Override
public Set<K> keySet() {
sort();
return map.keySet();
}
@Override
public Set<Entry<K, V>> entrySet() {
sort();
return map.entrySet();
}
@Override
public Collection<V> values() {
sort();
return map.values();
}
private void sort() {
if (!isSortingNeeded) {
return;
}
List<Entry<K, V>> list = new ArrayList<>(size());
for (Iterator<Map.Entry<K, V>> it = map.entrySet().iterator(); it.hasNext();) {
Map.Entry<K, V> entry = it.next();
list.add(entry);
it.remove();
}
Collections.sort(list);
for (Entry<K, V> entry : list) {
map.put(entry.getKey(), entry.getValue());
}
isSortingNeeded = false;
}
@Override
public String toString() {
sort();
return map.toString();
}
}
(4)Guavaは ImmutableMap.Builder.orderEntriesByValue(Comparator valueComparator)を提供しています 結果として得られるマップは不変になりますが
で指定されたコンパレータに従って値でエントリを順序付けるようにこのBuilderを構成します。
ソート順は安定しています。つまり、2つのエントリの値がで同等と比較される場合、最初に挿入されたエントリは構築マップの反復順序の最初のになります。