etc./StackOverFlow

지도 정렬<Key, Value> 값으로

청렴결백한 만능 재주꾼 2022. 1. 6. 11:11
반응형

질문자 :Community Wiki


저는 Java를 처음 접했고 종종 Map<Key, Value> 를 정렬해야 한다는 사실을 알게 되었습니다.

값이 고유하지 않기 때문에 keySetarray 로 변환하고 키와 연결된 값을 정렬 하는 사용자 지정 비교기 를 사용하여 배열 정렬 을 통해 해당 배열을 정렬합니다.

더 쉬운 방법이 있습니까?



다음은 일반 친화적인 버전입니다.

 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; } }

Community Wiki

중요 사항:

이 코드는 여러 가지 방법으로 중단될 수 있습니다. 제공된 코드를 사용하려는 경우 주석을 읽고 그 의미를 인식해야 합니다. 예를 들어, 값은 더 이상 해당 키로 검색할 수 없습니다. ( 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}

Community Wiki

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);

Community Wiki

3개의 1줄 답변...

Google Collections Guava 를 사용하여 이 작업을 수행합니다. 값이 Comparable 이면 다음을 사용할 수 있습니다.

 valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))

이는 맵을 위한 함수(객체)를 생성하고 [키 중 하나를 입력으로 사용하여 해당 값을 반환하는] 그런 다음 [값]에 자연적인(비교 가능한) 순서를 적용합니다.

비교할 수 없는 경우 다음과 같은 조치를 취해야 합니다.

 valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))

이것들은 TreeMap( Ordering Comparator 확장함에 따라) 또는 정렬 후 LinkedHashMap에 적용될 수 있습니다.

주의 : TreeMap을 사용하려는 경우 비교 == 0이면 항목이 이미 목록에 있음을 기억하십시오(동일한 값을 비교하는 여러 값이 있는 경우 발생). 이를 완화하기 위해 다음과 같이 비교기에 키를 추가할 수 있습니다(키와 값이 Comparable 이라고 가정).

 valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())

= 키로 매핑된 값에 자연스러운 순서를 적용하고 키의 자연스러운 순서와 결합합니다.

키가 0과 비교되면 여전히 작동하지 않지만 대부분의 comparable 항목에 hashCode , equalscompareTo 가 종종 동기화되기 때문에...)

Ordering.onResultOf()Functions.forMap() 을 참조하십시오.

구현

이제 우리는 원하는 것을 수행하는 비교기를 얻었으므로 결과를 가져와야 합니다.

 map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

이제 이것이 작동할 가능성이 가장 높지만 다음과 같습니다.

  1. 완성된 지도가 주어지면 완료되어야 합니다.
  2. TreeMap 위의 비교기를 시도하지 마십시오. 넣기가 끝날 때까지 값이 없을 때 삽입된 키를 비교하는 것은 의미가 없습니다. 즉, 정말 빨리 깨집니다.

포인트 1은 나에게 약간의 거래 차단기입니다. google 컬렉션은 엄청나게 게으릅니다(좋은 점: 거의 모든 작업을 즉시 수행할 수 있습니다. 실제 작업은 결과를 사용하기 시작할 때 완료됨). 이를 위해서는 전체 지도를 복사해야 합니다!

"전체" 답변/값별 라이브 정렬 지도

그래도 걱정하지 마십시오. 이러한 방식으로 "라이브" 지도를 정렬하는 데 집착했다면 다음과 같은 미친 것으로 위의 문제 중 하나가 아닌 둘 모두(!) 해결할 수 있습니다.

참고: 이것은 2012년 6월에 크게 변경되었습니다. 이전 코드는 절대 작동할 수 없습니다. 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));

Community Wiki

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; }

Community Wiki

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));

Community Wiki

키를 정렬하려면 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();

마지막으로, 정렬된 정보에 지속적으로 액세스해야 하는 경우(간헐적으로 정렬하는 것이 아니라) 추가 멀티 맵을 사용할 수 있습니다. 자세한 내용이 필요하면 알려주세요...


Community Wiki

commons-collections 라이브러리에는 TreeBidiMap 이라는 솔루션이 포함되어 있습니다. 또는 Google Collections API를 살펴볼 수 있습니다. 사용할 수있는 TreeMultimap이 있습니다.

그리고 이러한 프레임워크를 사용하지 않으려면... 소스 코드와 함께 제공됩니다.


Community Wiki

주어진 답변을 살펴 보았지만 많은 키가 동일한 값을 가질 때 필요 이상으로 복잡하거나 맵 요소를 제거합니다.

다음은 더 적합하다고 생각되는 솔루션입니다.

 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; }

지도는 가장 높은 값에서 가장 낮은 값으로 정렬됩니다.


Community Wiki

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(); }

반환된 iterable은 반복될 때마다 지정된 맵의 새로운 스냅샷을 생성하므로 동시 수정을 제외하고 항상 맵의 현재 상태를 반영합니다.


Community Wiki

사용자 정의 비교기를 생성하고 새로운 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}

Community Wiki

주어진 지도

 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}

{작업=200, 기회=200, 미국=100, 기술=70, 소프트웨어=50}


Community Wiki

지도를 정렬해야 하는 끊임없는 필요성은 아마도 냄새일 것이라는 점에는 동의하지만 다음 코드가 다른 데이터 구조를 사용하지 않고 이를 수행하는 가장 쉬운 방법이라고 생각합니다.

 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 개체의 정렬된 목록입니다.


Community Wiki

다음과 같은 일반 비교기를 사용하십시오.

 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)); } }

Community Wiki

가장 많이 투표된 답변은 동일한 항목이 2개 있는 경우 작동하지 않습니다. TreeMap은 동일한 값을 남겨둡니다.

예: 정렬되지 않은 지도

키/값: D/67.3
키/값: A/99.5
키/값: B/67.4
키/값: C/67.5
키/값: E/99.5

결과

키/값: A/99.5
키/값: C/67.5
키/값: B/67.4
키/값: D/67.3

그래서 E는 생략!!

나를 위해 비교기를 조정하는 것이 잘 작동했습니다. 같으면 0이 아니라 -1을 반환합니다.

예에서:

클래스 ValueComparator 구현 Comparator {

지도 기반; public ValueComparator(지도 기반) { this.base = 기본; }

공개 int 비교(객체 a, 객체 b) {

 if((Double)base.get(a) < (Double)base.get(b)) { return 1; } else if((Double)base.get(a) == (Double)base.get(b)) { return -1; } else { return -1; }

} }

이제 다음을 반환합니다.

정렬되지 않은 맵:

키/값: D/67.3
키/값: A/99.5
키/값: B/67.4
키/값: C/67.5
키/값: E/99.5

결과:

키/값: A/99.5
키/값: E/99.5
키/값: C/67.5
키/값: B/67.4
키/값: D/67.3

Aliens에 대한 응답으로(2011년 11월 22일): 정수 ID 및 이름의 맵에 이 솔루션을 사용하고 있지만 아이디어는 동일하므로 위의 코드가 올바르지 않을 수 있습니다(테스트에서 작성하겠습니다. 올바른 코드 제공) 위의 솔루션을 기반으로 한 지도 정렬 코드입니다.

 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개의 경우(지도)에 필요했습니다.


Community Wiki

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 를 사용합니다. 이렇게 하면 모든 맵 항목이 세 번 복사됩니다. 한 번은 맵에서 임시 목록(LinkedList 또는 ArrayList)으로, 그런 다음 임시 배열로, 마지막으로 새 맵으로 복사됩니다.

내 솔루션은 불필요한 LinkedList를 생성하지 않기 때문에 이 한 단계를 생략합니다. 다음은 일반적이고 성능이 최적화된 코드입니다.

 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; }

Community Wiki

이것은 중복 값이 있는 경우 작동하지 않는 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을 처리하는 방법은 아직 공개되지 않았습니다.

이 접근 방식의 중요한 이점 중 하나는 여기에 제공된 다른 솔루션과 달리 실제로 Map을 반환한다는 것입니다.


Community Wiki

최선의 접근

 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

Community Wiki

늦은 입장.

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()

Community Wiki

주요 문제. 첫 번째 답변을 사용하는 경우(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 }

Community Wiki

이 질문에 대한 답변은 이미 많이 있지만, 내가 찾던 것, 연결된 값으로 정렬된 키와 항목을 반환하고 지도에서 키와 값이 수정될 때 이 속성을 유지 관리하는 지도 구현을 제공한 것은 없습니다. 다른가지 질문 이 이에 대해 구체적으로 묻습니다.

이 사용 사례를 해결하는 일반적인 친숙한 예제를 만들었습니다. 이 구현은 원래 객체의 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; } }

Community Wiki

컨텍스트에 따라 항목이 맵에 배치되는 순서를 기억하는 java.util.LinkedHashMap<T> Collections.sort() 를 통해 정렬할 수 있는 별도의 List를 유지 관리하는 것이 좋습니다.


Community Wiki

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()); } }

Community Wiki

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 에 목록 을 넣고 싶을 수도 있지만 바로 반복하려는 경우에는 불필요합니다...


Community Wiki

이것은 너무 복잡합니다. 지도는 값을 기준으로 정렬하는 것과 같은 작업을 수행하지 않아야 합니다. 가장 쉬운 방법은 요구 사항에 맞게 자신의 클래스를 만드는 것입니다.

아래의 예에서는 *가 있는 위치에 TreeMap에 비교기를 추가해야 합니다. 그러나 Java API에 의해 비교기는 값이 아닌 키만 제공합니다. 여기에 언급된 모든 예는 2 지도를 기반으로 합니다. 하나의 해시와 하나의 새로운 트리. 이상합니다.

예:

 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() ); }

Community Wiki

Java 8 이상에서 모든 맵을 정렬하는 간단한 방법

 Map<String, Object> mapToSort = new HashMap<>(); List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet()); Collections.sort(list, Comparator.comparing(o -> o.getValue().getAttribute())); HashMap<String, Object> sortedMap = new LinkedHashMap<>(); for (Map.Entry<String, Object> map : list) { sortedMap.put(map.getKey(), map.getValue()); }

Java 7 이하를 사용하는 경우

 Map<String, Object> mapToSort = new HashMap<>(); List<Map.Entry<String, Object>> list = new LinkedList<>(mapToSort.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Object>>() { @Override public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { return o1.getValue().getAttribute().compareTo(o2.getValue().getAttribute()); } }); HashMap<String, Object> sortedMap = new LinkedHashMap<>(); for (Map.Entry<String, Object> map : list) { sortedMap.put(map.getKey(), map.getValue()); }

Community Wiki

@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 }

Community Wiki

다음은 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 ); } }

이로써 퍼블릭 도메인에 기부되었습니다.


Community Wiki

중복 값이 있는 쌍으로 정렬된 맵을 갖기 위한 몇 가지 간단한 변경 사항입니다. 비교 메서드(클래스 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; } } }

Community Wiki

확실히 Stephen의 솔루션은 정말 훌륭하지만 Guava를 사용할 수 없는 사람들을 위해:

다음은 값 맵으로 정렬하는 솔루션입니다. 이 솔루션은 동일한 값 등이 두 번 있는 경우를 처리합니다.

 // 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

그것이 어떤 사람들에게 도움이되기를 바랍니다.


Community Wiki

출처 : http:www.stackoverflow.com/questions/109383/sort-a-mapkey-value-by-values

반응형