1450

저는 Java에 비교적 익숙하지 않습니다.Map<Key, Value>값에.

값이 고유하지 않기 때문에 나는keySet~로array, 그리고 그 배열을 통해 정렬배열 정렬~와사용자 지정 비교기키와 관련된 값을 정렬합니다.

더 쉬운 방법이 있습니까?


  • 지도는 정렬되지 않지만 빠른 액세스가 가능합니다. 객체 동일 값은 맵의 제약 조건을 위반합니다. 다음과 같이 항목 세트를 사용하십시오.List<Map.Entry<...>> list =new LinkedList(map.entrySet())Collections.sort ....그런 식으로. - Hannes
  • Java에서 카운터를 사용하려고 할 때 발생할 수있는 경우입니다 (Map < Object, Integer >). 발생 횟수 별 정렬은 일반적인 작업입니다. 파이썬과 같은 언어에는 카운터 데이터 구조가 내장되어 있습니다. Java로 구현하는 다른 방법으로,이리예제가있다. - demongolem
  • 소팅 된 맵에는 많은 유스 케이스가 있으므로 JDK에서 TreeMap 및 ConcurrentSkipListMap을 사용하는 이유가 많습니다. - alobodzk

30 답변


813

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

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


  • 다행이 도움이됩니다. John, LinkedHashMap은 예측 가능한 반복 순서를 제공하므로 솔루션에서 중요합니다. - Carter Page
  • @ buzz3791 참. 모든 정렬 알고리즘의 경우가 될 것입니다. 정렬 중에 구조의 노드 값을 변경하면 예측할 수없는 (거의 항상 나쁜) 결과가 생성됩니다. - Carter Page
  • @Sheagorath 나는 안드로이드에서 그것을 시도하고 너무 작동합니다. Java 6 버전을 사용하는 경우 플랫폼 관련 문제는 아닙니다. 구현 했습니까?유사한가치 객체에서 올바르게 나타 납니까? - saiyancoder
  • Java 8 버전을 사용하지 않아야합니다.forEachOrdered대신에forEach, 이후의 문서forEach상태 : "이 연산의 동작은 명시 적으로 비 결정적입니다." - rob
  • 이 글을 완전히 찢어 버렸지 만, 댓글에 @CarterPage를 적어주었습니다 (어쨌든 오픈 소스 프로젝트에 포함될 것입니다). 정말 고마워. - Nathan Beach

407

중요 사항:

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


  • 더 이상 (stackoverflow.com/questions/109383/…). 또한, 왜 더블에 던지기가 있었습니까? 방금 있어야할까요?return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))? - Stephen
  • @Stephen : 아니요.이 경우 모든 값이 같은 키는 모두 삭제됩니다 (참조와 같음과 비교의 차이). 또한 :이 코드조차 다음 순서에 문제가 있습니다.map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d); - steffen
  • 트리 맵에 사용되는 콤퍼레이터는 equals와 일치하지 않습니다 (sortMap javadox를 참조). 즉, 트리 맵에서 항목을 퇴장 시키면 작동하지 않습니다. sorted_map.get ( "A")는 null을 리턴합니다. 이것은 트리 맵의 사용이 망가 졌음을 의미합니다. - mR_fr0g
  • 사람들에게 명확하지 않은 경우를 대비하여, 동일한 값으로 여러 개의 키를 매핑하는 경우이 솔루션은 원하는대로 할 수 없습니다. 정렬 된 결과에 해당 키 중 하나만 표시됩니다. - Maxy-B
  • Louis Wasserman (예, Google 구아바 친구들 중 한 명)은 실제로이 답변을 싫어합니다. "재미 있다고 생각하면 여러 가지 혼란을 야기합니다. 지지지도가 변경되면 중단됩니다. 여러 개의 키가 동일한 값으로 매핑되면 끊어집니다. 당신이 뒷받침지도에없는 열쇠에 타기를 요청하면 그것은 깨질 것입니다. 맵에없는 키 (Map.equals 호출, containsKey 등)에서 조회가 발생하게하는 것이 무엇이든하면 정말 이상한 스택 추적으로 중단됩니다. "plus.google.com/102216152814616302326/posts/bEQLDK712MJ - haylem

247

자바 8은 엔트리를 스트림으로 변환하고 Map.Entry의 콤비네이터를 사용하여 새로운 답을 제시한다.

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue());

이렇게하면 항목을 값의 오름차순으로 정렬하여 소비 할 수 있습니다. 내림차순 값을 원하면 간단히 비교기를 반대로하십시오.

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));

값이 비교 가능하지 않으면 명시 적 비교자를 전달할 수 있습니다.

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(comparator));

그런 다음 다른 스트림 작업을 사용하여 데이터를 사용할 수 있습니다. 예를 들어, 새지도에서 상위 10 개를 원할 경우 :

Map<K,V> topTen =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
       .limit(10)
       .collect(Collectors.toMap(
          Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

또는에 인쇄하십시오.System.out:

map.entrySet().stream()
   .sorted(Map.Entry.comparingByValue())
   .forEach(System.out::println);


  • 좋아,하지만parallelStream()이 경우에는? - Benj
  • 병렬로 작동하지만 부분 결과를 결합하기 위해지도를 병합하는 데 드는 비용이 너무 비싸고 병렬 버전이 사용자의 희망에 부합하지 않을 수도 있습니다. 그러나 그것은 작동하고 정확한 답을 산출합니다. - Brian Goetz
  • 유용한 조언을 주셔서 감사합니다. 그것이 당신이 사용하는 키의 유형과 많은 매개 변수에 달려 있지만 그것은 정확하게 내가 궁금해하는 것이 었습니다 ... 중요한 것은 " 작동하고 올바른 답을 산출합니다 "입니다. - Benj
  • 다른 크기의 목록 인 값에 따라 정렬하려면 어떻게해야합니까? 목록 크기를 내림차순으로 정렬하고 싶습니다. - Pete
  • 돈 ' 당신은 top10 예제에서 compareByValue를 사용해야합니까? - Leo

208

세 줄의 답이 세 개 있습니다 ...

나는Google 소식 모음 구아바이 작업을 수행하려면 - 값이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 월에 크게 바뀌 었습니다. 이전 코드는 작동하지 않을 수 있습니다. 내부 HashMap은 코드 사이에 무한 루프를 만들지 않고 값을 조회해야합니다.TreeMap.get()->compare()compare()->get()

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import com.google.common.base.Functions;
import com.google.common.collect.Ordering;

class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
    //A map for doing lookups on the keys for comparison so we don't get infinite loops
    private final Map<K, V> valueMap;

    ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
        this(partialValueOrdering, new HashMap<K,V>());
    }

    private ValueComparableMap(Ordering<? super V> partialValueOrdering,
            HashMap<K, V> valueMap) {
        super(partialValueOrdering //Apply the value ordering
                .onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
                .compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
        this.valueMap = valueMap;
    }

    public V put(K k, V v) {
        if (valueMap.containsKey(k)){
            //remove the key in the sorted set before adding the key again
            remove(k);
        }
        valueMap.put(k,v); //To get "real" unsorted values for the comparator
        return super.put(k, v); //Put it in value order
    }

    public static void main(String[] args){
        TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
        map.put("a", 5);
        map.put("b", 1);
        map.put("c", 3);
        assertEquals("b",map.firstKey());
        assertEquals("a",map.lastKey());
        map.put("d",0);
        assertEquals("d",map.firstKey());
        //ensure it's still a map (by overwriting a key, but with a new value) 
        map.put("d", 2);
        assertEquals("b", map.firstKey());
        //Ensure multiple values do not clobber keys
        map.put("e", 2);
        assertEquals(5, map.size());
        assertEquals(2, (int) map.get("e"));
        assertEquals(2, (int) map.get("d"));
    }
 }

넣으면 해시 맵에 비교 자의 값이 있는지 확인한 다음 정렬을 위해 TreeSet에 놓습니다. 하지만 그 전에는 키가 실제로 중복되지 않았 음을 확인하기 위해 해시 맵을 확인합니다. 또한 우리가 생성하는 콤퍼레이터에는 키가 포함되어 있으므로 중복 된 값이 비 중복 키를 삭제하지 않습니다 (== 비교로 인해). 이 2 개의 항목은치명적인지도 계약을 지키기위한 것; 네가 그렇게 생각하지 않는다면지도를 완전히 뒤집을 시점에있다.Map<V,K>).

생성자는 다음과 같이 호출되어야합니다.

 new ValueComparableMap(Ordering.natural());
 //or
 new ValueComparableMap(Ordering.from(comparator));


  • 안녕하세요 @ 스테판, 주문을 사용하는 방법을 보여 줄 수 있습니까? Ordering의 소스 코드를 살펴보면 .natural (). onResultOf (...)가 반환하는 것을 완전히 파악할 수 없습니다! 소스 코드는 "public < F > < F > 주문 onResultOf " , 나는 그것이 어떻게 컴파일되는지를 알지 못한다. 가장 중요한 " < F > 주문하기 < F > " 지도를 정렬하려면? 그것은 비교 자인가? 감사. - smallufo
  • Ordering단순히 부유하다.Comparator. 각 예제 (각 밑의 기울임 꼴)에 주석을 달았습니다. "천연" 객체가Comparable; 그것은 apache common의 ComparableComparator와 같습니다.onResultOf비교할 항목에 함수를 적용합니다. 정수에 1을 더한 함수가 있다면natural().onResultOf(add1Function).compare(1,2)끝낼거야.2.compareTo(3) - Stephen
  • IllegalSortedMap.copyOf는, 원의 맵에 중복 한 값이있는 경우에 IllegalArgumentException를 Throw합니다. - lbalazscs
  • @Ibalazscs 예, 가능합니다. - 사용할 수 있어야합니다.ImmutableSetMultiMap또는ImmutableListMultiMap중복 변수의 모음을 포함합니다. - Stephen
  • 고맙습니다. 한 솔루션에서 솔루션을 사용했습니다. 맵에 문제가 있다고 생각합니다. 맵과 같이 동작하려면 이전에 키와 연관된 값을 반환해야합니다 (있는 경우).하지만 이렇게하면 절대 수행하지 않습니다. 내가 사용한 솔루션은 제거 된 값이 있으면 반환하는 것입니다. - alex

175

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


  • 소트 될리스트는 "new LinkedList"이다. 말. 고맙게도 Collections.sort ()는 이런 종류의 에러를 피하기 위해리스트를 먼저 배열에 덤프합니다 (하지만 여전히 ArrayList를 배열에 덤프하는 것은 LinkedList에 대해 동일한 작업을 수행하는 것보다 빠릅니다). - Dimitris Andreou
  • Iterator에서 TernaryTree로 변환 할 수 없습니다 .Iterator - lisak
  • @ gg.kaspersky "LinkedList를 정렬하는 것이 좋지 않습니다"라고 말하는 것은 아니지만 LinkedList 자체는 정렬에 관계없이 나쁜 선택입니다.많은ArrayList를 사용하는 것이 더 좋으며, 추가 포인트의 경우 정확하게 map.size ()에서 크기를 조정하는 것이 좋습니다. 참조code.google.com/p/memory-measurer/wiki/…ArrayList의 요소 당 평균 비용 : 5 바이트 LinkedList의 요소 당 평균 비용 : 24 바이트. 정확한 크기의 ArrayList의 경우 평균 비용은 4 바이트입니다. 즉, LinkedList는ArrayList에 필요한 메모리의 양을 곱합니다. 그것은 단지 팽창이다. - Dimitris Andreou
  • 위의 값을 사용하여 오름차순으로 정렬되었습니다. 내림차순으로 정렬하는 방법? - ram
  • 내림차순으로 정렬하려면 o1과 o2를 교체하십시오. - Soheil

39

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


  • 역순으로 정렬하는 방법? - Vlad Holubiev
  • 해결책을 찾았습니다 -Collections.reverseOrder(comparing(Entry::getValue)) - Vlad Holubiev
  • 나는 거기에 오타가 있다고 생각합니다. "" to 맵 " " Collectors.toMap () " ? - Jake Stokes
  • @ JakeStokes 또는 정적 가져 오기 사용 :-) - assylias
  • 엔트리 값을 역순으로 정렬하는 더 좋은 방법은 다음과 같습니다.Entry.comparingByValue(Comparator.reverseOrder()) - Gediminas Rimsa

30

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

마지막으로 정렬 된 정보에 계속 액세스해야하는 경우 (한 번만 정렬하는 것보다) 추가 멀티 맵을 사용할 수 있습니다. 더 자세한 정보가 필요하면 알려주세요.


  • 두 번째 버전은 List < Map.Entry < K, V > >를 반환하면 더욱 간결해질 수 있습니다. 또한지도를 추가로 많이 만들 필요없이 키와 값을 반복하고 가져 오는 것이 더 쉽습니다. 이 코드는 스레드가 안전하지 않다는 가정하에 모두 가정합니다. 배킹 맵 또는 정렬 된 목록이 다중 스레드 환경에서 공유되면 모든 배팅이 해제됩니다. - Mike Miller

25

commons-collections 라이브러리에는 다음과 같은 솔루션이 있습니다.TreeBidiMap. 또는 Google Collections API를 살펴볼 수도 있습니다. 그것은 가지고있다.TreeMultimap당신이 사용할 수 있습니다.

그리고이 프레임 워크를 사용하고 싶지 않다면 소스 코드가 있습니다.


  • commons-collection을 사용할 필요가 없습니다. Java는 자체 java.util.TreeMap과 함께 제공됩니다. - yoliho
  • 그렇습니다. 그러나 TreeMap은 정렬시 훨씬 유연성이 떨어집니다.mapentries의 일부. - p3t0r
  • BidiMap의 문제는 관계를 변환 할 수 있도록 (즉, 키와 값이 모두 고유해야 함) 키와 값 사이에 1 : 1 관계 제한을 추가한다는 것입니다. 즉, 단어 개수가 많은 단어가 같은 개수이므로 단어 개수 객체와 같은 것을 저장하는 데 사용할 수 없습니다. - Doug

23

주어진 답을 살펴 봤지만, 많은 키가 필요한 것보다 복잡하거나 여러 개의 키가 같은 값일 때지도 요소를 제거합니다.

여기에 맞는 솔루션이 더 적합하다고 생각합니다.

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

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


  • 문제 : 반환 된 맵을 나중에 사용하려는 경우 (예 : 특정 요소가 포함되어 있는지 확인하려는 경우) 사용자 정의 비교기 때문에 항상 false가 반환됩니다. 가능한 해결책 : 마지막 라인을 다음으로 대체하십시오 : return new LinkedHashMap < K, V > (sortedByValues); - Erel Segal-Halevi
  • 이것은 @ErelSegalHalevi가 지적한 사실을 제외하고 나에게 깨끗한 해결책으로 보인다. Comparator를 지정한 것처럼 맵에 값이 존재하는지 여부를 검사한다. map.put ( "1", "One"); map.put ( "2", "Two"); map.put ( "3", "Three"); map.put ( "4", "Four"); map.put ( "5", "Five"); 새로운 TreeMap < K, V > (sortedByValues)를 반환하는 것처럼 sortByValues () 함수에서 새로운 객체를 반환하면 map.containsKey ( "1")는 항상 false를 반환합니다. 문제를 해결합니다. 감사 Abhi - abhi
  • user157196 및 Carter Page의 답변과 거의 같습니다. Carter Page의 LinkedHashMap 픽스가 포함되어 있습니다. - Kirby
  • 솔루션의 4 번째 줄은 int = compare.get (k1) .compareTo (map.get (k2)); 오름차순이 필요한 경우 - cosmolev

17

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

엔트리는, 지정된 Comparator를 사용해 값에 의해 순서 붙일 수 있습니다. 또는 값이 서로 비교 가능하다면 명시 적 비교기가 필요하지 않습니다.

<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
    return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}

돌려 주어지는리스트는,이 메소드가 불려 갔을 때의 지정된 맵의 snapshot입니다. 어느 쪽도, 다음의 변경을 반영하지 않습니다. 지도의 실시간 반복보기 :

<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
    return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}

리턴 된 iterable은 iterated 될 때마다 주어진 맵의 새로운 스냅 샷을 생성하므로 동시 변경을 막을 때 항상 맵의 현재 상태를 반영합니다.



14

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

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


  • 이 방법은 Map < V, List < K > > 거의 같은 효과를 지닌 오브젝트. 값은 Map 객체의 키가 아니기 때문에 실제로 찾고있는 것은 IMHO입니다. - Jeff Wu
  • 이 솔루션은 많은 가치를 가지고 작동하지 않으며, 내 수 (각 키와 관련된 값) - Sam Levin
  • 그것은 이상합니다. 당신은 정교 할 수 있습니까? 당신의 결과는 무엇이고 기대했던 결과는 무엇입니까? - Lyudmil

14

커스터마이즈 된 컴퍼 레이터를 작성해, 새로운 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}



11

귀하가 동등한 2 개의 항목을 가지고있을 때 가장 많이 투표 한 답변은 효과가 없습니다. TreeMap은 동일한 값을 유지합니다.

예 : 정렬되지 않은지도

key/value: D/67.3
key/value: A/99.5
key/value: B/67.4
key/value: C/67.5
key/value: E/99.5

결과들

key/value: A/99.5
key/value: C/67.5
key/value: B/67.4
key/value: D/67.3

그래서 밖으로 E!

나를 위해 괜찮아요 0, -1 반환하지 않는 경우 equals 비교기를 조정하려면 작동합니다.

예제에서 :

ValueComparator 클래스는 Comparator {

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

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

}     }

이제 반환 :

정렬되지 않은지도 :

key/value: D/67.3
key/value: A/99.5
key/value: B/67.4
key/value: C/67.5
key/value: E/99.5

결과 :

key/value: A/99.5
key/value: E/99.5
key/value: C/67.5
key/value: B/67.4
key/value: D/67.3

외계인에 대한 응답으로 (2011 년 22 월 22 일) : 나는이 솔루션을 Integer ID와 이름의 맵에 사용하고 있지만 생각은 동일하므로 위의 코드가 올바르지 않을 수도 있습니다 (테스트에서 작성하고 올바른 코드를 제공합니다). 이것이 코드입니다. 위의 솔루션을 기반으로지도 정렬 :

package nl.iamit.util;

import java.util.Comparator;
import java.util.Map;

public class Comparators {


    public static class MapIntegerStringComparator implements Comparator {

        Map<Integer, String> base;

        public MapIntegerStringComparator(Map<Integer, String> base) {
            this.base = base;
        }

        public int compare(Object a, Object b) {

            int compare = ((String) base.get(a))
                    .compareTo((String) base.get(b));
            if (compare == 0) {
                return -1;
            }
            return compare;
        }
    }


}

그리고 이것은 테스트 클래스입니다 (방금 테스트 해 보았습니다. Integer, String Map에서 작동합니다 :

package test.nl.iamit.util;

import java.util.HashMap;
import java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;

public class TestComparators {


    @Test
    public void testMapIntegerStringComparator(){
        HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
        Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
                unSoretedMap);
        TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
        //the testdata:
        unSoretedMap.put(new Integer(1), "E");
        unSoretedMap.put(new Integer(2), "A");
        unSoretedMap.put(new Integer(3), "E");
        unSoretedMap.put(new Integer(4), "B");
        unSoretedMap.put(new Integer(5), "F");

        sorted_map.putAll(unSoretedMap);

        Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
        Object[] currecntKeys=sorted_map.keySet().toArray();

        assertArrayEquals(targetKeys,currecntKeys);
    }
}

다음은지도의 Comparator 코드입니다.

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 케이스 (지도)


  • 그것은 나를 위해 작동하지 않습니다. 나는 모든 값을 null로 얻는다. - Aliens
  • 당신이 옳았어요, 제가 처음에 준 코드에 약간의 오류가있었습니다! 내 최근 편집이 도움이되기를 바랍니다. - michel.iamit

11

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

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


9

사용하는 대신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;
}


8

이것은 앤서니 (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을 반환한다는 것입니다.


  • 잘못된 값이있는 경우 제 방법이 작동합니다. " 1 "을 사용하는 100 개가 넘는 키가있는지도에서는이 기능을 사용했습니다. 값으로. - Anthony

7

주요 문제. 첫 번째 대답을 사용하면 (Google이 여기에 표시됩니다) 비교기를 변경하여 등호를 추가합니다. 그렇지 않으면 키별로 sorted_map에서 값을 가져올 수 없습니다.

public int compare(String a, String b) {
        if (base.get(a) > base.get(b)) {
            return 1;
        } else if (base.get(a) < base.get(b)){
            return -1;
        } 

        return 0;
        // returning 0 would merge keys
    }


  • 이제 같은 값을 가진 두 개의 항목을 추가 할 때 병합됩니다. 객체가 동일 (같음) 인 경우 0 만 반환해야합니다. - Masood_mj

6

최선의 접근법

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


6

이미이 질문에 대한 답변이 많이 있지만, 내가 원하는 것을 제공하지 못했습니다. 키와 항목을 연관된 값으로 정렬하여 반환하는지도 구현이며 키와 값이지도에서 수정 될 때이 속성을 유지 관리합니다. 두다른 질문들이것을 구체적으로 요구하십시오.

이 사용 사례를 해결하는 일반적인 친숙한 예제를 만들었습니다. 이 구현은 Map 인터페이스의 모든 계약 (예 : keySet () 및 setSet ()에서 반환 된 값 변경 및 제거를 원래 객체로 반영하는 것과 같은)을 준수하지 않습니다. 스택 오버플로 (Stack Overflow) 답변에 포함하기에는 너무 큰 해결책이라고 느꼈습니다. 좀 더 완벽한 구현을 만들 수 있다면 아마도 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;
    }
}


5

컨텍스트에 따라java.util.LinkedHashMap<T>항목이지도에 배치되는 순서를 기억합니다. 그렇지 않으면 자연 순서에 따라 값을 정렬해야하는 경우에는 다음을 통해 정렬 할 수있는 별도의 목록을 유지하는 것이 좋습니다.Collections.sort().


  • LinkedHashMap이 아마도 저에게 가장 좋은 솔루션 일 것입니다. 이제는 LinkedHashMap을 버리고 새로운 LinkedHashMap을 만드는 것이 얼마나 비싼 지 알아보기 위해 노력하고 있습니다. - NobleUplift

5

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

넣고 싶을지도 모릅니다.명부안에연결된 해시 맵, 그러나 만약 당신이 단지 그것을 반복해서 반복한다면, 그것은 불필요한 것입니다 ...


  • 맞았지만 비교기가 같음 값을 처리하지 못함 - Sebastien Lorber

5

이것은 너무 복잡합니다. 지도는 가치로 분류하는 것과 같은 일을하지 않아야했습니다. 가장 쉬운 방법은 요구 사항에 맞는 맞춤 클래스를 만드는 것입니다.

아래 예제에서 *가있는 곳에 비교기 TreeMap을 추가해야합니다. 하지만 자바 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;
    }
}

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


4

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


  • 그렇다면 아마도 더 나은 해결책은 자체 정렬 맵을 사용하는 것입니다.이 경우에는 org.apache.commons.collections.bidimap.TreeBidiMap을 사용하십시오. - Maxim Veksler

4

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

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


4

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


4

중복 된 값을 가진 쌍으로 정렬 된 맵을 가지기위한 간단한 변경. 값이 동일 할 때 비교 메소드 (클래스 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;
        }
      }
    }


4

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

희망은 그것이 도움이 될 것입니다 일부 사람들


3

키가 중복되고 작은 데이터 집합 (<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 문에서.

가장 빠른 정렬이 아니지만 추가 종속성없이 작업을 수행합니다.


3

user157196과 Carter Page의 솔루션을 병합했습니다.

class MapUtil {

    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue( Map<K, V> map ){
        ValueComparator<K,V> bvc =  new ValueComparator<K,V>(map);
        TreeMap<K,V> sorted_map = new TreeMap<K,V>(bvc);
        sorted_map.putAll(map);
        return sorted_map;
    }

}

class ValueComparator<K, V extends Comparable<? super V>> implements Comparator<K> {

    Map<K, V> base;
    public ValueComparator(Map<K, V> base) {
        this.base = base;
    }

    public int compare(K a, K b) {
        int result = (base.get(a).compareTo(base.get(b)));
        if (result == 0) result=1;
        // returning 0 would merge keys
        return result;
    }
}


3

Guava의 멀티 맵을 사용해보십시오.

TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
        Multimaps.invertFrom(Multimaps.forMap(originalMap), 
        ArrayListMultimap.<Integer, String>create()).asMap());

결과적으로 원래 값에서 해당 값에 해당하는 키 모음으로 맵을 가져옵니다. 동일한 값에 대해 여러 개의 키가있는 경우에도이 방법을 사용할 수 있습니다.

연결된 질문


관련된 질문

최근 질문