값으로 맵 <키, 값> 정렬


1634

나는 Java를 처음 접했고 종종 Map<Key, Value>값 을 정렬해야한다는 것을 알았습니다 .

값이 고유하지 때문에, 나 자신이 변환 찾을 keySetarray, 그리고 통해 해당 배열 정렬 배열 정렬 A를 사용자 정의 비교 값의 종류가 키와 연관된 것으로합니다.

더 쉬운 방법이 있습니까?


24
지도는 정렬하기위한 것이 아니라 빠르게 액세스하는 것입니다. 객체 등가 값은 맵의 제약 조건을 위반합니다. 마찬가지로, 엔트리 세트를 사용 List<Map.Entry<...>> list =new LinkedList(map.entrySet())하고 Collections.sort ....그것을 그런 식으로.
Hannes

1
Java에서 카운터를 사용하려고 할 때 이런 상황이 발생할 수 있습니다 (Map <Object, Integer>). 그러면 발생 횟수를 기준으로 정렬하는 것이 일반적인 작업입니다. Python과 같은 언어에는 카운터 데이터 구조가 내장되어 있습니다. 자바 구현의 다른 방법의 경우, 여기에 예입니다
demongolem는

6
정렬 된 맵에는 많은 사용 사례가 있으므로 jdk에 TreeMap 및 ConcurrentSkipListMap이 있습니다.
alobodzk


1
TreeMap 및 ConcurrentSkipListMap은 키별로 정렬됩니다. 문제는 값을 기준으로 정렬하는 것입니다.
피터

답변:


899

일반적인 버전은 다음과 같습니다.

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

10
도움이되어 기쁘다. John, LinkedHashMap은 예측 가능한 반복 순서를 제공하므로 솔루션에 중요합니다.
카터 페이지

3
맞습니다. 그것은 어떤 정렬 알고리즘에서도 마찬가지입니다. 정렬하는 동안 구조의 노드 값을 변경하면 예측할 수없고 거의 항상 나쁜 결과가 생성됩니다.
카터 페이지

3
@ Sheagorath Android에서 사용해 보았으며 작동합니다. Java 6 버전을 사용한다는 점을 고려하면 플랫폼 별 문제는 아닙니다. Value 객체에서 Comparable을 올바르게 구현 했습니까 ?
saiyancoder

6
상태 문서가 "이 작업의 동작은 명시 적으로 결정적이지 않기 때문에 " forEachOrdered대신 Java 8 버전을 사용하지 않아야 합니까? forEachforEach
rob

1
완전히 이것을 찢어 버렸지 만 의견에 @CarterPage를 인정했습니다 (어쨌든 오픈 소스 프로젝트에있을 것입니다). 정말 고마워.
Nathan Beach

419

중요 사항:

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

18
더 이상 없습니다 ( stackoverflow.com/questions/109383/… ). 또한 왜 Double에게 캐스트가 있었습니까? 그냥 안 return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))그래?
Stephen

12
@Stephen : 아니요.이 경우 값과 같은 모든 키가 삭제됩니다 (참조와 같음과 비교의 차이). 또한 :이 코드조차도 다음 순서에 문제가 있습니다 map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
steffen

43
트리 맵에 사용 된 비교기는 같지 않습니다 (sortMap javadox 참조). 이것은 트리 맵에서 아이템을 폐기하는 것이 작동하지 않음을 의미합니다. sorted_map.get ( "A")는 null을 반환합니다. 이는 트리 맵 사용이 중단되었음을 의미합니다.
mR_fr0g

87
사람들에게 명확하지 않은 경우를 대비하여 :이 솔루션은 여러 키를 같은 값으로 매핑하는 경우 원하는 작업을 수행하지 않을 수 있습니다. 이러한 키 중 하나만 정렬 결과에 나타납니다.
Maxy-B

63
Louis Wasserman (예, Google Guava 사용자 중 한 명)은 실제로이 답변을 매우 싫어합니다. 백킹 맵에없는 키에 전화를 걸면 키가 끊어집니다. 키가 아닌 키에서 조회를 수행 할 수있는 작업을 수행하면 "Map.equals 호출, containsKey 등 모든 맵이 정말 이상한 스택 추적으로 중단됩니다." plus.google.com/102216152814616302326/posts/bEQLDK712MJ
haylem

339

Java 8은 새로운 답변을 제공합니다. 항목을 스트림으로 변환하고 Map.Entry의 비교기 결합기를 사용하십시오.

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

이를 통해 오름차순으로 정렬 된 항목을 사용할 수 있습니다. 내림차순 값을 원하면 비교기를 반대로 바꾸십시오.

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

값을 비교할 수없는 경우 명시 적 비교기를 전달할 수 있습니다.

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

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

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

또는 다음으로 인쇄하십시오 System.out.

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

근데이 parallelStream()경우에 사용하는 것은 어떻습니까?
Benj

11
병렬로 작동하지만 부분 결과를 결합하기 위해 맵을 병합하는 데 드는 비용이 너무 비싸고 병렬 버전이 원하는대로 작동하지 않을 수 있습니다. 그러나 그것은 효과가 있고 정답을 만듭니다.
Brian Goetz

유용한 조언에 감사드립니다. 사용하는 키 유형과 많은 매개 변수에 따라 다르지만 정확히 궁금했습니다. 중요한 것은 "작동하고 정답을 생성합니다"입니다.
Benj

2
top10 예제에서 compareByValue를 사용할 필요가 없습니까?
Leo

1
@ 벤지는 상위 10 개를 추출하는 관점에서 작동하지만 결과지도는 더 이상 주문되지 않습니다.
OrangeDog 2016 년

211

세 줄로 된 답변 ...

내가 사용하는 것이 구글 컬렉션 구아바을 이렇게 - 당신의 가치가있는 경우 Comparable다음 사용할 수 있습니다

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

그러면지도에 대한 함수 (객체)를 생성하고 [키를 입력으로 가져 와서 각각의 값을 반환] 자연적인 (비교 가능한) 순서를 [값]에 적용합니다.

그들이 비교할 수 없다면, 당신은의 라인을 따라 무언가를해야합니다

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

이것들은 Ordering확장 된 TreeMap Comparator이나 정렬 후 LinkedHashMap에 적용될 수 있습니다.

NB : TreeMap을 사용하려는 경우 비교 == 0 인 경우 항목이 이미 목록에 있습니다 (동일하게 비교되는 여러 값이있는 경우 발생 함). 이를 완화하기 위해 키와 값이 같다고 가정하여 키를 비교기에 추가 할 수 있습니다 Comparable.

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

= 키로 매핑 된 값에 자연 순서를 적용하고 자연 순서로 키를 조합합니다.

이 여전히 키를 0으로 비교, 그러나 이것은 대부분 충분합니다 경우 작동하지 않습니다 참고 comparable항목 (로 hashCode, equals그리고 compareTo동기화 종종 ...)

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

이행

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

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

이제 이것은 대부분 작동하지만 다음과 같이 작동합니다.

  1. 완성 된지도를 완성해야합니다
  2. 위의 비교기를 시도하지 마십시오 TreeMap. 퍼팅 이후까지 값이 없을 때 삽입 된 키를 비교하려고하는 것은 아무 의미가 없습니다.

포인트 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에 넣습니다. 그러나 그 전에 해시 맵을 검사하여 키가 실제로 복제본이 아님을 확인합니다. 또한 우리가 만든 비교기에는 키가 포함되므로 중복 값이 ​​중복되지 않은 키를 삭제하지 않습니다 (== 비교로 인해). 이 두 항목은 지도 계약을 유지하는 데 필수적 입니다. 당신이 그것을 원하지 않는다고 생각한다면, 당신은 거의지도를 완전히 뒤집는 지점에 Map<V,K>있습니다.

생성자를 다음과 같이 호출해야합니다.

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

안녕 @ Stephen, 당신은 주문을 사용하는 방법을 예를 들어 줄 수 있습니까? Ordering의 소스 코드를 살펴본 결과 .natural (). onResultOf (...)가 무엇을 반환하는지 완전히 알 수 없습니다! 소스 코드는 "public <F> Ordering <F> onResultOf"입니다. 심지어 컴파일 방법을 모르겠습니다! 가장 중요한 것은 "<F> Ordering <F>"를 사용하여지도를 정렬하는 방법? 비교기입니까? 감사.
smallufo

Ordering단순히 부자 Comparator입니다. 각 예제에 주석을 달았습니다 (각 기울임 꼴 아래). "자연"은 객체가 Comparable; 아파치 커먼의 ComparableComparator와 같습니다. onResultOf비교할 항목에 기능을 적용합니다. 따라서 정수에 1을 더한 함수가 있다면 natural().onResultOf(add1Function).compare(1,2)결국 끝날 것입니다2.compareTo(3)
Stephen

원본 맵에 중복 값이 ​​있으면 ImmutableSortedMap.copyOf에서 IllegalArgumentException이 발생합니다.
lbalazscs

@Ibalazscs 그렇습니다- 중복 변수 모음 을 사용 ImmutableSetMultiMap하거나 ImmutableListMultiMap포함 할 수 있어야 합니다.
Stephen

1
덕분에 하나의 프로젝트에서 솔루션을 사용했습니다. 그래도 문제가 있다고 생각합니다.지도처럼 행동하려면 이전에 키와 관련된 값을 반환해야합니다. 내가 사용한 해결책은 제거 된 값이 있으면 반환하는 것입니다.
alex

185

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

16
정렬 할 목록은 "new LinkedList"입니다. ?? 사람. 고맙게도 Collections.sort ()는 이러한 종류의 오류를 정확하게 피하기 위해 먼저 목록을 배열에 덤프합니다 (그러나 여전히 ArrayList를 배열에 덤프하는 것은 LinkedList에 대해 수행하는 것보다 빠릅니다).
Dimitris Andreou

반복자에서 TernaryTree.Iterator로 변환 할 수 없습니다
lisak

4
@ gg.kaspersky "LinkedList를 정렬하는 것이 나쁘다"고 말하지는 않지만 LinkedList 자체는 정렬에 관계없이 여기에서 나쁜 선택입니다. ArrayList를 사용하는 것이 훨씬 좋으며 추가 포인트의 경우 정확히 map.size ()에서 크기를 조정하십시오. 또한 참조 code.google.com/p/memory-measurer/wiki/... 의 ArrayList의 요소마다 평균 비용을 24 바이트 : 5 LinkedList의에서 요소마다 평균 비용 바이트. 정확한 크기의 ArrayList의 경우 평균 비용은 4 바이트입니다. 즉, LinkedList는 ArrayList에 필요한 메모리 양의 6 배를 사용합니다. 그것은 단지 팽창의
디미트리 Andreou

2
위의 값을 사용하여 오름차순으로 정렬되었습니다. 내림차순으로 정렬하는 방법?
ram

1
내림차순으로 정렬하려면 o1과 o2를 교체하십시오.
Soheil

68

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 2018 년

6
해결책을 발견 -Collections.reverseOrder(comparing(Entry::getValue))
블라드 Holubiev에게

1
오타가있는 것 같습니다. "toMap"을 "Collectors.toMap ()"으로 호출해서는 안됩니다.
Jake Stokes

1
@JakeStokes 정적 가져 오기를 :-) 사용 또는
assylias

6
역순으로 항목 값을 기준으로 정렬 더 좋은 방법은 다음과 같습니다Entry.comparingByValue(Comparator.reverseOrder())
게디 미나스 Rimsa

31

키를 정렬하려면 비교기가 각 비교에 대한 각 값을 찾아야합니다. 더 확장 가능한 솔루션은 entrySet을 직접 사용하므로 각 비교에 대해 값을 즉시 사용할 수 있기 때문에 (숫자별로 백업하지는 않았지만).

다음은 그러한 것들의 일반적인 버전입니다.

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
    final int size = map.size();
    final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
    list.addAll(map.entrySet());
    final ValueComparator<V> cmp = new ValueComparator<V>();
    Collections.sort(list, cmp);
    final List<K> keys = new ArrayList<K>(size);
    for (int i = 0; i < size; i++) {
        keys.set(i, list.get(i).getKey());
    }
    return keys;
}

private static final class ValueComparator<V extends Comparable<? super V>>
                                     implements Comparator<Map.Entry<?, V>> {
    public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

위의 솔루션에 대한 메모리 회전을 줄이는 방법이 있습니다. 생성 된 첫 번째 ArrayList는 예를 들어 반환 값으로 재사용 될 수 있습니다. 이것은 일부 제네릭 경고를 억제해야하지만 재사용 가능한 라이브러리 코드에는 가치가 있습니다. 또한 모든 호출에서 비교기를 다시 할당 할 필요는 없습니다.

덜 매력적인 버전이지만 더 효율적입니다.

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
    final int size = map.size();
    final List reusedList = new ArrayList(size);
    final List<Map.Entry<K, V>> meView = reusedList;
    meView.addAll(map.entrySet());
    Collections.sort(meView, SINGLE);
    final List<K> keyView = reusedList;
    for (int i = 0; i < size; i++) {
        keyView.set(i, meView.get(i).getKey());
    }
    return keyView;
}

private static final Comparator SINGLE = new ValueComparator();

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


List <Map.Entry <K, V >>를 반환하면 두 번째 버전이 더 간결해질 수 있습니다. 또한 맵에 많은 추가 작업을 수행하지 않고도 키와 값을 반복하고 쉽게 얻을 수 있습니다. 이 코드는 스레드 안전하지 않은 것으로 가정합니다. 지지 맵 또는 정렬 된 목록이 멀티 스레드 환경에서 공유되면 모든 베팅이 해제됩니다.
Mike Miller

26

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

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


공용 모음을 사용할 필요는 없습니다. Java에는 자체 java.util.TreeMap이 제공됩니다.
yoliho

2
예,하지만 MapMapries 의 부분을 정렬 할 때 TreeMap은 훨씬 유연 합니다.
p3t0r

9
BidiMap의 문제점은 키와 값 사이에 1 : 1 관계 제약 조건을 추가하여 관계를 뒤집을 수 없게 만드는 것입니다 (즉, 키와 값 모두 고유해야 함). 이것은 많은 단어가 같은 수를 가지므로 이것을 사용하여 단어 수 개체와 같은 것을 저장할 수 없음을 의미합니다.
Doug

26

주어진 답변을 살펴 보았지만 여러 키가 동일한 값을 가질 때 필요한 것보다 더 복잡하거나 맵 요소를 제거합니다.

다음은 더 잘 맞는 솔루션입니다.

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

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


6
문제 : 나중에 반환 된지도를 사용하여 (예 : 특정 요소가 포함되어 있는지 확인하려는 경우) 사용자 지정 비교기로 인해 항상 거짓이됩니다! 가능한 해결책 : 마지막 줄을 다음과 같이 바꾸십시오 : return new LinkedHashMap <K, V> (sortedByValues);
Erel Segal-Halevi

@ErelSegalHalevi가 지적하고 비교기에 지정한 값이 맵에 있는지 여부를 확인한다는 사실을 제외하고는 깨끗한 해결책으로 보입니다. map.put ( "1", "One"); map.put ( "2", "Two"); map.put ( "3", "Three"); map.put ( "4", "4"); map.put ( "5", "5"); map.containsKey ( "1")은 return new TreeMap <K, V> (sortedByValues)와 같이 sortByValues ​​() 함수에서 새 객체를 반환하면 항상 false를 반환합니다. 문제를 해결합니다. 고마워요 Abhi
abhi

user157196 및 Carter Page의 답변과 거의 동일합니다. 카터 페이지에는 LinkedHashMap 수정 프로그램이 포함되어 있습니다
Kirby

솔루션의 네 번째 줄은 int compare = map.get (k1) .compareTo (map.get (k2)); 오름차순 주문이 필요한 경우
www.Decompiler.com

19

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은 반복 될 때마다 지정된 맵의 새로운 스냅 샷을 생성하므로 동시 수정을 제외하면 항상 맵의 현재 상태를 반영합니다.


값별로 정렬 된 맵이 아닌 항목 목록을 반환합니다. 지도를 반환하는 다른 버전 : stackoverflow.com/a/22132422/829571
assylias

17

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

값이 같은 경우 키를 비교하기 위해 "return 1"줄을 변경했습니다. "return ((String) o1) .compareTo ((String) o2);"
gjgjgj

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

이 솔루션은 (값이 각 키와 관련된) 내 계산과 나사, 값이 많이 작동하지 않습니다
샘 레빈을

1
이상하다. 좀 더 자세히 설명해 주시겠습니까? 당신의 결과물은 무엇이며 당신이 예상 한 결과물은 무엇입니까?
Lyudmil

12

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

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

11

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 {

지도베이스; 공개 ValueComparator (Map base) {this.base = base; }

public int compare (Object a, Object b) {

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

}}

이제는 다음을 반환합니다.

분류되지 않은지도 :

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

결과 :

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

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

package nl.iamit.util;

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

public class Comparators {


    public static class MapIntegerStringComparator implements Comparator {

        Map<Integer, String> base;

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

        public int compare(Object a, Object b) {

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


}

그리고 이것은 테스트 클래스입니다 (방금 테스트했으며 Integer, String Map에서 작동합니다.

package test.nl.iamit.util;

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

public class TestComparators {


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

        sorted_map.putAll(unSoretedMap);

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

        assertArrayEquals(targetKeys,currecntKeys);
    }
}

다음은 맵 비교기의 코드입니다.

public static class MapStringDoubleComparator implements Comparator {

    Map<String, Double> base;

    public MapStringDoubleComparator(Map<String, Double> base) {
        this.base = base;
    }

    //note if you want decending in stead of ascending, turn around 1 and -1
    public int compare(Object a, Object b) {
        if ((Double) base.get(a) == (Double) base.get(b)) {
            return 0;
        } else if((Double) base.get(a) < (Double) base.get(b)) {
            return -1;
        }else{
            return 1;
        }
    }
}

그리고 이것은 이것에 대한 테스트 케이스입니다.

@Test
public void testMapStringDoubleComparator(){
    HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
    Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
            unSoretedMap);
    TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
    //the testdata:
    unSoretedMap.put("D",new Double(67.3));
    unSoretedMap.put("A",new Double(99.5));
    unSoretedMap.put("B",new Double(67.4));
    unSoretedMap.put("C",new Double(67.5));
    unSoretedMap.put("E",new Double(99.5));

    sorted_map.putAll(unSoretedMap);

    Object[] targetKeys={"D","B","C","E","A"};
    Object[] currecntKeys=sorted_map.keySet().toArray();

    assertArrayEquals(targetKeys,currecntKeys);
}

cource의 당신은 이것을 훨씬 더 일반적인 것으로 만들 수 있지만, 나는 단지 1 경우 (지도)에 필요했습니다.


당신이 옳았습니다. 처음에 준 코드에 약간의 오류가있었습니다! 최근 편집 내용이 도움이 되길 바랍니다.
michel.iamit

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

이것은 앤서니의 대답의 변형이며 중복 값이 ​​있으면 작동하지 않습니다.

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"인 100 개 이상의 키가있는 맵에서 사용했습니다.
Anthony

8

최고의 접근법

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

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

7

이 질문에 대한 답은 이미 많지만, 내가 찾던 것을 제공하지 못했습니다. 키와 항목을 관련 값으로 정렬하여 반환하고 맵에서 키와 값이 수정 될 때이 속성을 유지합니다. 구체적으로 다른가지 질문 이 있습니다.

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

Comparable과 Comparator가 허용되지 않으면 어떻게해야합니까?
Ved Prakash 17 년

유스 케이스를 이해했는지 잘 모르겠다면 정교하게 설명 할 수 있습니다. 값으로 사용하려는 객체가 비교 가능하지 않은 경우 해당 객체로 변환해야합니다.
David Bleckmann 19

6

늦은 입장.

Java-8이 등장하면서 스트림을 매우 쉽고 간결하게 데이터 조작에 사용할 수 있습니다. 스트림을 사용하여 값별로 맵 항목을 정렬하고 삽입 순서 반복 을 유지 하는 LinkedHashMap 을 작성할 수 있습니다 .

예 :

LinkedHashMap sortedByValueMap = map.entrySet().stream()
                .sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey))     //first sorting by Value, then sorting by Key(entries with same value)
                .collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);

역순으로 주문하려면 다음을 교체하십시오.

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()

이 댓글 버전에 감사드립니다. 질문 하나 : 사용의 차이는 무엇입니까 Entry.comparingByValue()(assylias은 위의 답변으로 stackoverflow.com/a/22132422/1480587 ) 또는 comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)당신이 사용하는 것이가? 값이 동일한 경우 키를 비교한다는 것을 알고 있습니다. 정렬이 동일한 값을 가진 요소의 순서를 유지한다는 것을 알았습니다. 키가 이미 정렬 된 경우 키를 기준으로 정렬해야합니까?
피터 T.

6

주어진지도

   Map<String, Integer> wordCounts = new HashMap<>();
    wordCounts.put("USA", 100);
    wordCounts.put("jobs", 200);
    wordCounts.put("software", 50);
    wordCounts.put("technology", 70);
    wordCounts.put("opportunity", 200);

값을 기준으로 오름차순으로지도 정렬

Map<String,Integer>  sortedMap =  wordCounts.entrySet().
                                                stream().
                                                sorted(Map.Entry.comparingByValue()).
        collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    System.out.println(sortedMap);

값을 기준으로 내림차순으로지도 정렬

Map<String,Integer>  sortedMapReverseOrder =  wordCounts.entrySet().
            stream().
            sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).
            collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    System.out.println(sortedMapReverseOrder);

산출:

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

{jobs = 200, 기회 = 200, USA = 100, 기술 = 70, 소프트웨어 = 50}


나는 map-reduce가 정말로 "감소"했다고 생각한다 .... 좋은 해결책.
ha9u63ar

감사 @ ha9u63ar
Arpan Saini

작동하지만 HashMap에서 요소 순서가 어떻게 작동하는지 이해하지 못합니까?
알리 투

5

상황에 따라 java.util.LinkedHashMap<T>어떤 항목을 사용하여 항목이 맵에 배치되는지를 기억합니다. 그렇지 않으면 자연 순서에 따라 값을 정렬 해야하는 경우을 통해 정렬 할 수있는 별도의 List를 유지하는 것이 좋습니다 Collections.sort().


왜 이것이 -1인지 알지 못합니다. 지금까지 LinkedHashMap이 아마도 가장 좋은 해결책 일 것입니다. 버려지고 새로운 LinkedHashMap을 만드는 데 얼마나 비싼 지 알아 내려고 노력하고 있습니다.
NobleUplift

5

이후 트리 맵은 <> 작동하지 않는 동일 할 수있는 값에 대해, I이 사용 :

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

당신은 넣어 할 수 있습니다 목록을 A의 의 LinkedHashMap ,하지만 당신은 단지 바로 반복하는 거라면, 그 여분의 ...


맞습니다.하지만 비교기는 같은 값을 처리하지 않습니다
Sebastien Lorber

5

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

예를 들어 아래에서는 *가있는 곳에 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()
        );
}

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에서, 자기 정렬 맵을 사용하는 것입니다
맥심 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 문에서>를 <로 변경하십시오.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.