java 자바 값으로 Map<Key, Value> 정렬




자바 맵 value 정렬 (24)

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

아래 예제에서 *가있는 곳에 비교기 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()
        );
}

나는 Java에 비교적 익숙하지 않고 Map<Key, Value> 를 정렬해야하는 경우가 종종 있습니다.

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

더 쉬운 방법이 있습니까?


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

최선의 접근법

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

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

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


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

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

키가 중복되고 작은 데이터 집합 (<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를 if로 변경하십시오.

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


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

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

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

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

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

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


중요 사항:

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

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

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


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 될 때마다 주어진 맵의 새로운 스냅 샷을 생성하므로 동시 변경을 금지하고 맵의 현재 상태를 항상 반영합니다.


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

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


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

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

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

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

그러면지도에 대한 함수 (객체)가 만들어지고 [입력 값으로 키를 가져 와서 해당 값을 반환하는], 자연스러운 (비교 가능한) 순서 [값]이 적용됩니다.

만약 그들이 비교할 수 없다면, 당신은 라인을 따라 뭔가를 할 필요가 있습니다.

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

이것들은, TreeMap ( Ordering extends Comparator ), 또는 소트 후의 LinkedHashMap에 적용 할 수 있습니다.

NB : 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은 나를위한 약간의 거래 차단기이다; 구글 콜렉션은 믿을 수 없을 정도로 게으르다. (어느 것이 든 순식간에 모든 작업을 수행 할 수 있으며 결과를 사용할 때 실제 작업이 완료된다.) 전체 맵을 복사해야한다.

"전체"답변 / 값별로 실시간 정렬 된지도

그래도 걱정하지 마십시오. 이런 식으로 정렬 된 "라이브"맵을 가지고 사로 잡히면 위의 문제 중 하나만 아니라 둘 모두를 다음과 같이 미친 듯이 해결할 수 있습니다.

참고 : 이것은 2012 년 6 월에 크게 변경되었습니다. 이전 코드는 작동하지 않습니다. 내부 HashMap은 TreeMap.get() -> compare()compare() -> get() 사이에 무한 루프를 만들지 않고 값을 조회해야합니다 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을 완전히 뒤집을 시점에 있습니다 ( Map<V,K> ).

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

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

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


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


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

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

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

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

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


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

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

귀하가 동등한 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

Aliens (2011 nov. 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 케이스 (지도)


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

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


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

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


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






collections