私は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;
}
}
このコードは、複数の方法で破損する可能性があります。提供されたコードを使用する場合は、コメントも読んで意味を理解してください。たとえば、キーで値を取得できなくなりました。(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
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は、私にとってはちょっとした問題です。グーグルコレクションは信じられないほど怠惰です(これは良いことです:ほとんどすべての操作を瞬時に行うことができます;実際の作業は結果の使用を開始したときに行われます)、これにはマップ全体をコピーする必要があります!
でも心配しないでください。この方法で「ライブ」マップを並べ替えることに夢中になっている場合は、上記の問題の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));
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));
キーを並べ替えるには、Comparator が比較ごとに各値を検索する必要があります。よりスケーラブルなソリューションは、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 を戻り値として再利用できます。これには、いくつかのジェネリック警告を抑制する必要がありますが、再利用可能なライブラリ コードには価値があるかもしれません。また、呼び出しごとに Comparator を再割り当てする必要はありません。
魅力的ではありませんが、より効率的なバージョンを次に示します。
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;
}
マップは最高値から最低値にソートされることに注意してください。
カスタマイズされたコンパレータを作成し、新しい 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 final 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 回コピーされます。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's answer のバリエーションであり、重複する値がある場合は機能しません:
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 を返すことです。
最善のアプローチ
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
}
コンテキストに応じて、java.util.LinkedHashMap<T>
アイテムがマップに配置される順序を記憶します。それ以外の場合、自然な順序に基づいて値を並べ替える必要がある場合は、 を介して並べ替えることができる別のリストを維持することをお勧めしCollections.sort()
ます。
これはあまりにも複雑です。マップは、値でソートするような仕事をするべきではありませんでした。最も簡単な方法は、要件に合わせて独自のクラスを作成することです。
下の例では、* の場所に 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()
);
}
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に入れたいと思うかもしれませんが、すぐに反復するだけなら、それは余計です...
@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
}
重複する値を持つペアを持つ並べ替えられたマップを作成するためのいくつかの簡単な変更。比較メソッド (クラス 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;
}
}
}
これは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 );
}
}
これにより、パブリックドメインに寄付されました。
重複したキーと小さなデータ セット (<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 ステートメントで > を < に変更します。
最速の並べ替えではありませんが、追加の依存関係なしでジョブを実行します。
Java で最も簡単な方法でハッシュマップを並べ替えます。ツリーマップやリストなどに保存する必要はありません。
ここでは、Java Streams を使用します。
このマップを値で並べ替えます (昇順)
Map<String, Integer> mp= new HashMap<>();
mp.put("zebra", 1);
mp.put("blossom", 2);
mp.put("gemini", 3);
mp.put("opera", 7);
mp.put("adelaide", 10);
Map<String, Integer> resultMap= mp.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e1, LinkedHashMap::new));
高度な for ループや反復子を使用するなど、複数の方法で並べ替えられた resultMap を出力できるようになりました。
上記のマップは、値の降順で並べ替えることもできます
Map<String, Integer> resultMap= mp.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e1, LinkedHashMap::new));
「ユーザー」をマップに保存し、「ユーザー」の「名前」に基づいて昇順 (辞書式) に並べ替える別のシナリオを考えてみましょう。
User u1= new User("hi", 135);
User u2= new User("bismuth", 900);
User u3= new User("alloy", 675);
User u4= new User("jupiter", 342);
User u5= new User("lily", 941);
Map<String, User> map2= new HashMap<>();
map2.put("zebra", u3);
map2.put("blossom", u5);
map2.put("gemini", u1);
map2.put("opera", u2);
map2.put("adelaide", u4);
Map<String, User> resultMap=
map2.entrySet().stream().sorted(Map.Entry.<String, User>comparingByValue( (User o1, User o2)-> o1.getName().compareTo(o2.getName()))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e2, LinkedHashMap::new));
class User
{
String name;
int id;
public User(String name, int id) {
super();
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User [name=" + name + ", id=" + id + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
これに直面したとき、私は横にリストを作成するだけです。それらをカスタム 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(); }
}
の最も単純な力ずくのsortHashMap
方法HashMap<String, Long>
:コピーして貼り付けて、次のように使用できます。
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;
}
}
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);
}
}
出力=
//sort by values
3 arun singh
1 bandagi singh
28 jakarta singh
2 panipat singh
6 vikram singh
5 vinay singh
public class SortedMapExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("Cde", "C");
map.put("Abc", "A");
map.put("Cbc", "Z");
map.put("Dbc", "D");
map.put("Bcd", "B");
map.put("sfd", "Bqw");
map.put("DDD", "Bas");
map.put("BGG", "Basd");
System.out.println(sort(map, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}}));
}
@SuppressWarnings("unchecked")
public static <K, V> Map<K,V> sort(Map<K, V> in, Comparator<? super V> compare) {
Map<K, V> result = new LinkedHashMap<K, V>();
V[] array = (V[])in.values().toArray();
for(int i=0;i<array.length;i++)
{
}
Arrays.sort(array, compare);
for (V item : array) {
K key= (K) getKey(in, item);
result.put(key, item);
}
return result;
}
public static <K, V> Object getKey(Map<K, V> in,V value)
{
Set<K> key= in.keySet();
Iterator<K> keyIterator=key.iterator();
while (keyIterator.hasNext()) {
K valueObject = (K) keyIterator.next();
if(in.get(valueObject).equals(value))
{
return valueObject;
}
}
return null;
}
}
//ここで試してください。値の並べ替えのコードを変更しています。
キーを並べ替えるには、TreeMapを使用したより良い解決策を見つけました(値ベースの並べ替えの解決策も用意しようと思います)。
public static void main(String[] args) {
Map<String, String> unsorted = new HashMap<String, String>();
unsorted.put("Cde", "Cde_Value");
unsorted.put("Abc", "Abc_Value");
unsorted.put("Bcd", "Bcd_Value");
Comparator<String> comparer = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}};
Map<String, String> sorted = new TreeMap<String, String>(comparer);
sorted.putAll(unsorted);
System.out.println(sorted);
}
出力は次のようになります。
{Abc = Abc_Value、Bcd = Bcd_Value、Cde = Cde_Value}
さて、このバージョンは 2 つの新しい Map オブジェクトと 2 つの反復と値の並べ替えで動作します。マップ エントリを 2 回ループする必要がありますが、うまく機能することを願っています。
public static void main(String[] args) {
Map<String, String> unsorted = new HashMap<String, String>();
unsorted.put("Cde", "Cde_Value");
unsorted.put("Abc", "Abc_Value");
unsorted.put("Bcd", "Bcd_Value");
Comparator<String> comparer = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}};
System.out.println(sortByValue(unsorted, comparer));
}
public static <K, V> Map<K,V> sortByValue(Map<K, V> in, Comparator<? super V> compare) {
Map<V, K> swapped = new TreeMap<V, K>(compare);
for(Entry<K,V> entry: in.entrySet()) {
if (entry.getValue() != null) {
swapped.put(entry.getValue(), entry.getKey());
}
}
LinkedHashMap<K, V> result = new LinkedHashMap<K, V>();
for(Entry<V,K> entry: swapped.entrySet()) {
if (entry.getValue() != null) {
result.put(entry.getValue(), entry.getKey());
}
}
return result;
}
このソリューションでは、Comparator で TreeMap を使用し、すべての null キーと値を並べ替えます。最初に、TreeMap の順序付け機能を使用して値を並べ替えます。次に、並べ替えられた Map を使用して結果を作成します。保持される LinkedHashMap は値の順序が同じです。
グリーツ、ガハド
マップのサイズよりも大きな値がない場合は、配列を使用できます。これが最速のアプローチです。
public List<String> getList(Map<String, Integer> myMap) {
String[] copyArray = new String[myMap.size()];
for (Entry<String, Integer> entry : myMap.entrySet()) {
copyArray[entry.getValue()] = entry.getKey();
}
return Arrays.asList(copyArray);
}
このようにマップを並べ替えるだけです
Map<String, String> unsortedMap = new HashMap<String, String>();
unsortedMap.put("E", "E Val");
unsortedMap.put("F", "F Val");
unsortedMap.put("H", "H Val");
unsortedMap.put("B", "B Val");
unsortedMap.put("C", "C Val");
unsortedMap.put("A", "A Val");
unsortedMap.put("G", "G Val");
unsortedMap.put("D", "D Val");
Map<String, String> sortedMap = new TreeMap<String, String>(unsortedMap);
System.out.println("\nAfter sorting..");
for (Map.Entry <String, String> mapEntry : sortedMap.entrySet()) {
System.out.println(mapEntry.getKey() + " \t" + mapEntry.getValue());
Map 値が Comparable (例: String) を実装している場合、これは機能するはずです。
Map<Object, String> map = new HashMap<Object, String>();
// Populate the Map
List<String> mapValues = new ArrayList<String>(map.values());
Collections.sort(mapValues);
マップ値自体が Comparable を実装していないが、それらを並べ替えることができる Comparable のインスタンスがある場合は、最後の行を次のように置き換えます。
Collections.sort(mapValues, comparable);
java.util.TreeMapを使用します。
「マップは、使用されるコンストラクターに応じて、キーの自然順序付けに従って、またはマップ作成時に提供される Comparator に従ってソートされます。」