私は比較的Javaに慣れていません。Map<Key, Value>
値に。
値は一意ではないので、私は自分自身をkeySet
にarray
そして、その配列をソートする配列ソートとともにカスタムコンパレータそれはキーに関連した値でソートします。
もっと簡単な方法はありますか?
これは一般的なバージョンです。
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;
}
}
forEachOrdered
の代わりにforEach
のドキュメントforEach
「この操作の動作は明確に非決定的です。」 - rob
このコードは複数の方法で中断することがあります。提供されているコードを使用する予定がある場合は、その意味を理解するためにコメントも必ず読んでください。たとえば、値はそれらのキーによって取得できなくなります。 (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}
return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))
? - Stephenmap.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
- steffen
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);
parallelStream()
この場合 ? - Benj
1行の答えが3つ…
私は使うだろう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月に大幅に変更されました - 以前のコードは機能しませんでした。内部HashMapは、無限ループを作成せずに値を検索するために必要です。TreeMap.get()
- >compare()
そしてcompare()
- >get()
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つの項目はバイタル地図の契約が確実に守られるため。あなたがそれを望んでいないと思うのであれば、あなたはほぼ地図を完全に逆転させる段階にきています。Map<V,K>
)
コンストラクタは次のように呼び出す必要があります。
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
Ordering
単に金持ちですComparator
。それぞれの例(それぞれの下にあるイタリック体)をコメントしてみました。 "自然"オブジェクトがComparable
;これは、Apacheの一般的なComparableComparatorのようなものです。onResultOf
比較対象の項目に関数を適用します。したがって、整数に1を足した関数があれば、natural().onResultOf(add1Function).compare(1,2)
やってしまう2.compareTo(3)
- StephenImmutableSetMultiMap
またはImmutableListMultiMap
重複した変数のコレクションを格納します。 - Stephen
から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では、次のものを使用できます。ストリームapiかなり冗長ではありません。
Map<K, V> sortedMap = map.entrySet().stream()
.sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Collections.reverseOrder(comparing(Entry::getValue))
- Vlad HolubievEntry.comparingByValue(Comparator.reverseOrder())
- Gediminas Rimsa
キーを並べ替えるには、比較器が各比較の各値を調べる必要があります。値を各比較ですぐに利用できるようになるので、もっとスケーラブルな解決策は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}
あなたが等しい2つのアイテムを持っているとき、最も投票された答えはうまくいきません。 TreeMapは等しい値を除外します。
例: 未分類マップ
key/value: D/67.3 key/value: A/99.5 key/value: B/67.4 key/value: C/67.5 key/value: E/99.5
結果
key/value: A/99.5 key/value: C/67.5 key/value: B/67.4 key/value: 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; }
} }
今それが戻ります:
未分類の地図:
key/value: D/67.3 key/value: A/99.5 key/value: B/67.4 key/value: C/67.5 key/value: E/99.5
結果:
key/value: A/99.5 key/value: E/99.5 key/value: C/67.5 key/value: B/67.4 key/value: D/67.3
エイリアンへの応答として(2011年11月22日): 整数IDと名前のマップにこの解決策を使用していますが、考え方は同じですので、上記のコードは正しくない可能性があります(テストで書いて正しいコードを指定します)、これがコードです。上記の解決策に基づいて、マップのソートを行います。
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つのケースのためにそれを必要としました(地図)
次のような一般的なコンパレータを使用してください。
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));
}
}
使用する代わりに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回コピーされます。マップから一時リスト(LinkedListまたはArrayList)に1回、次に一時配列に、最後に新しいマップに1回コピーされます。
不要な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はあなたをここに連れて行って)、等しい句を追加するようにコンパレータを変更してください。さもなければあなたはキーでsorted_mapから値を得ることができません:
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
この質問にはすでに多くの答えがありますが、探しているもの、つまりキーとエントリを関連付けられた値でソートして返し、キーと値がマップ内で変更されるときにこのプロパティを維持するマップの実装は得られませんでした。二その他の 質問具体的にこれを求めてください。
このユースケースを解決する一般的なわかりやすい例を作り上げました。この実装は、値の変更を反映したり、元のオブジェクトの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;
}
入れたいかもしれませんリストで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()
);
}
@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
}
これはオブジェクト指向の解決策です(すなわち、使用しません)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 );
}
}
これによりパブリックドメインに寄付されました。
最もきれいな方法はマップを値でソートするためにコレクションを利用することです。
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());
}
}
重複した値を持つペアを持つソートマップを作成するためのいくつかの簡単な変更。 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;
}
}
}
確かにスティーブンの解決策は本当に素晴らしいですが、グアバを使用することができない人のために:
これはマップを値でソートするための私の解決策です。 この解決策は、同じ値が2倍ある場合などを扱います。
// 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
それが何人かの人々を助けることを願っています
重複するキーがあり、データのセットが少ない(< 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文の中で。
最速のソートではありませんが、追加の依存関係なしにジョブを実行します。
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;
}
}
Guavaのマルチマップを試すことができます。
TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
Multimaps.invertFrom(Multimaps.forMap(originalMap),
ArrayListMultimap.<Integer, String>create()).asMap());
その結果、元の値からそれらに対応するキーのコレクションへのマップが得られます。この方法は、同じ値に対して複数のキーがある場合でも使用できます。
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
そしてCollections.sort ....
それはそうです。 - Hannes