私はJavaに比較的慣れていないためMap<Key, Value>
、値を並べ替える必要があることがよくあります。
値が一意ではありませんので、私は自分自身が変換を見つけるkeySet
にarray
、そしてを通じてその配列のソート配列のソートとカスタムコンパレータキーに関連付けられた値でソートすること。
もっと簡単な方法はありますか?
私は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
このコードは複数の方法で破損する可能性があります。提供されたコードを使用する場合は、コメントも読んで、その影響を認識してください。たとえば、値はキーで取得できなくなります。(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)))
?
map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
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()
、この場合の使用についてはどうですか?
3つの1行回答...
私が使用するGoogleのコレクション グアバをこれを行うには-あなたの値がされている場合はComparable
、あなたが使用することができます
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
これは、マップの関数(オブジェクト)を作成し(任意のキーを入力として受け取り、それぞれの値を返します)、それらに自然な(比較可能な)順序を適用します[値]。
それらが比較できない場合、あなたは次の線に沿って何かをする必要があります
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
これらはTreeMap(Ordering
extends としてComparator
)またはLinkedHashMapにいくつかのソート後に適用できます
注: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
。挿入後まで値がない場合に挿入されたキーを比較しようとしても意味がありません。ポイント1は、私にとってちょっとした決断です。googleコレクションは信じられないほど怠惰です(これは良いことです:ほとんどすべての操作を瞬時に実行できます。実際の作業は結果の使用を開始すると行われます)。これにはマップ全体をコピーする必要があります。
でも心配しないでください。この方法で「ライブ」マップをソートすることに夢中なら、次のようなクレイジーな方法で上記の問題の1つではなく両方(!)を解決できます。
注:これは2012年6月に大幅に変更されました。以前のコードは機能しませんでした。- TreeMap.get()
> compare()
とcompare()
->の間に無限ループを作成せずに値を検索するには、内部HashMapが必要です。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 CommonのComparableComparatorのようなものです。onResultOf
比較されるアイテムに関数を適用します。したがって、整数に1を追加する関数がある場合、natural().onResultOf(add1Function).compare(1,2)
最終的には次のようになります2.compareTo(3)
ImmutableSetMultiMap
またはImmutableListMultiMap
格納できるはずです。
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))
Entry.comparingByValue(Comparator.reverseOrder())
キーをソートするには、コンパレータが各比較の各値を検索する必要があります。よりスケーラブルなソリューションでは、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コレクション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();
}
返された反復可能オブジェクトは、反復されるたびに指定されたマップの新しいスナップショットを作成するため、同時変更を禁止すると、常にマップの現在の状態が反映されます。
カスタマイズされたコンパレーターを作成し、それを新しい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}
マップをソートするための絶え間ない必要性はおそらく匂いであることに同意しますが、次のコードは、異なるデータ構造を使用せずにそれを行う最も簡単な方法だと思います。
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オブジェクトのソートされたリストで、そこからキーと値を取得できます。
次のような汎用コンパレータを使用します。
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(Map base){this.base = base; }
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 -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と名前のマップに使用していますが、考え方は同じであるため、上記のコードは正しくない可能性があります(テストで記述します)そしてあなたに正しいコードを与えてください)、これは上記の解決策に基づいたマップソートのコードです:
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;
}
}
}
これはテストクラスです(私はテストしたところ、これは整数、文字列マップで機能します:
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つは、ここで提供されている他のソリューションとは異なり、実際にマップを返すことです。
最善のアプローチ
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
大きな問題。最初の回答を使用する場合(Googleがここにアクセスします)、コンパレーターを変更して、equal句を追加します。そうしないと、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
}
この質問にはすでにたくさんの回答がありますが、私が探していたもの、つまり関連する値でソートされたキーとエントリを返し、キーと値がマップで変更されたときにこのプロパティを維持するマップ実装はどれも提供されませんでした。他の 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-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()
Entry.comparingByValue()
(assyliasは上記の答えとしてstackoverflow.com/a/22132422/1480587)またはcomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
あなたが使用していることは?値が同じ場合は、キーも比較していると思いますよね?並べ替えが同じ値の要素の順序を保持していることに気づきました。キーがすでに並べ替えられている場合、キーによる並べ替えが必要ですか?
与えられたマップ
Map<String, Integer> wordCounts = new HashMap<>();
wordCounts.put("USA", 100);
wordCounts.put("jobs", 200);
wordCounts.put("software", 50);
wordCounts.put("technology", 70);
wordCounts.put("opportunity", 200);
値に基づいてマップを昇順でソートします
Map<String,Integer> sortedMap = wordCounts.entrySet().
stream().
sorted(Map.Entry.comparingByValue()).
collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
System.out.println(sortedMap);
値に基づいてマップを降順にソートします
Map<String,Integer> sortedMapReverseOrder = wordCounts.entrySet().
stream().
sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).
collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
System.out.println(sortedMapReverseOrder);
出力:
{ソフトウェア= 50、テクノロジー= 70、アメリカ= 100、ジョブ= 200、機会= 200}
{jobs = 200、offportity = 200、USA = 100、technology = 70、software = 50}
コンテキストに応じて、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;
}
}
そしてコンパレータクラス:
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 );
}
}
これにより、パブリックドメインに寄付されました。
Afaikの最もクリーンな方法は、コレクションを利用してマップを値でソートすることです。
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());
}
}
重複する値を持つペアを含むマップをソートするためのいくつかの簡単な変更。比較メソッド(クラス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ステートメントで>を<に変更します。
最速の並べ替えではありませんが、追加の依存関係なしで作業を行います。
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
し、Collections.sort ....
それそのように。