나는 Java를 처음 접했고 종종 Map<Key, Value>
값 을 정렬해야한다는 것을 알았습니다 .
값이 고유하지 때문에, 나 자신이 변환 찾을 keySet
로 array
, 그리고 통해 해당 배열 정렬 배열 정렬 A를 사용자 정의 비교 값의 종류가 키와 연관된 것으로합니다.
더 쉬운 방법이 있습니까?
나는 Java를 처음 접했고 종종 Map<Key, Value>
값 을 정렬해야한다는 것을 알았습니다 .
값이 고유하지 때문에, 나 자신이 변환 찾을 keySet
로 array
, 그리고 통해 해당 배열 정렬 배열 정렬 A를 사용자 정의 비교 값의 종류가 키와 연관된 것으로합니다.
더 쉬운 방법이 있습니까?
답변:
일반적인 버전은 다음과 같습니다.
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;
}
}
forEachOrdered
대신 Java 8 버전을 사용하지 않아야 합니까? forEach
forEach
이 코드는 여러 가지 방법으로 중단 될 수 있습니다. 제공된 코드를 사용하려면 주석을 읽고 그 의미를 알고 있어야합니다. 예를 들어 키로 더 이상 값을 검색 할 수 없습니다. ( 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}
return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))
그래?
map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
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()
경우에 사용하는 것은 어떻습니까?
세 줄로 된 답변 ...
내가 사용하는 것이 구글 컬렉션 구아바을 이렇게 - 당신의 가치가있는 경우 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);
이제 이것은 대부분 작동하지만 다음과 같이 작동합니다.
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));
Ordering
단순히 부자 Comparator
입니다. 각 예제에 주석을 달았습니다 (각 기울임 꼴 아래). "자연"은 객체가 Comparable
; 아파치 커먼의 ComparableComparator와 같습니다. onResultOf
비교할 항목에 기능을 적용합니다. 따라서 정수에 1을 더한 함수가 있다면 natural().onResultOf(add1Function).compare(1,2)
결국 끝날 것입니다2.compareTo(3)
ImmutableSetMultiMap
하거나 ImmutableListMultiMap
포함 할 수 있어야 합니다.
에서 http://www.programmersheaven.com/download/49349/download.aspx
private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Object>() {
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
}
});
Map<K, V> result = new LinkedHashMap<>();
for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
result.put(entry.getKey(), entry.getValue());
}
return result;
}
Java 8에서는 스트림 API 를 사용하여 덜 장황한 방식으로 수행 할 수 있습니다 .
Map<K, V> sortedMap = map.entrySet().stream()
.sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Collections.reverseOrder(comparing(Entry::getValue))
Entry.comparingByValue(Comparator.reverseOrder())
키를 정렬하려면 비교기가 각 비교에 대한 각 값을 찾아야합니다. 더 확장 가능한 솔루션은 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();
마지막으로 한 번에 한 번만 정렬하지 않고 정렬 된 정보에 계속 액세스해야하는 경우 추가 다중 맵을 사용할 수 있습니다. 자세한 내용이 필요하면 알려주세요 ...
commons-collections 라이브러리에는 TreeBidiMap 이라는 솔루션이 포함되어 있습니다 . 또는 Google Collections API를 살펴볼 수 있습니다. 그것은이 TreeMultimap 당신이 사용할 수 있습니다.
이러한 프레임 워크를 사용하지 않으려면 소스 코드와 함께 제공됩니다.
주어진 답변을 살펴 보았지만 여러 키가 동일한 값을 가질 때 필요한 것보다 더 복잡하거나 맵 요소를 제거합니다.
다음은 더 잘 맞는 솔루션입니다.
public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
int compare = map.get(k2).compareTo(map.get(k1));
if (compare == 0) return 1;
else return compare;
}
};
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
지도는 가장 높은 값에서 가장 낮은 값으로 정렬됩니다.
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은 반복 될 때마다 지정된 맵의 새로운 스냅 샷을 생성하므로 동시 수정을 제외하면 항상 맵의 현재 상태를 반영합니다.
사용자 정의 된 비교기를 작성하고 새 TreeMap 오브젝트를 작성하는 동안 사용하십시오.
class MyComparator implements Comparator<Object> {
Map<String, Integer> map;
public MyComparator(Map<String, Integer> map) {
this.map = map;
}
public int compare(Object o1, Object o2) {
if (map.get(o2) == map.get(o1))
return 1;
else
return ((Integer) map.get(o2)).compareTo((Integer)
map.get(o1));
}
}
주요 기능에서 아래 코드를 사용하십시오.
Map<String, Integer> lMap = new HashMap<String, Integer>();
lMap.put("A", 35);
lMap.put("B", 75);
lMap.put("C", 50);
lMap.put("D", 50);
MyComparator comparator = new MyComparator(lMap);
Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator);
newMap.putAll(lMap);
System.out.println(newMap);
산출:
{B=75, D=50, C=50, A=35}
나는지도를 정렬 해야하는 끊임없는 냄새가 아마 냄새 일 것이라는 데 동의하지만 다음 코드는 다른 데이터 구조를 사용하지 않고 가장 쉬운 방법이라고 생각합니다.
public class MapUtilities {
public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
Collections.sort(entries, new ByValue<K, V>());
return entries;
}
private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
public int compare(Entry<K, V> o1, Entry<K, V> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
}
그리고 다음은 매우 불완전한 단위 테스트입니다.
public class MapUtilitiesTest extends TestCase {
public void testSorting() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("One", 1);
map.put("Two", 2);
map.put("Three", 3);
List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
assertEquals("First", "One", sorted.get(0).getKey());
assertEquals("Second", "Two", sorted.get(1).getKey());
assertEquals("Third", "Three", sorted.get(2).getKey());
}
}
결과는 키와 값을 얻을 수있는 Map.Entry 객체의 정렬 된 목록입니다.
다음과 같은 일반적인 비교기를 사용하십시오.
final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {
private 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));
}
}
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 경우 (지도)에 필요했습니다.
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;
}
이것은 앤서니의 대답의 변형이며 중복 값이 있으면 작동하지 않습니다.
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을 처리하는 방법은 오히려 공중에 있습니다.
이 방법의 한 가지 중요한 장점은 여기에 제공된 다른 솔루션과 달리 실제로지도를 반환한다는 것입니다.
최고의 접근법
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class OrderByValue {
public static void main(String a[]){
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("java", 20);
map.put("C++", 45);
map.put("Unix", 67);
map.put("MAC", 26);
map.put("Why this kolavari", 93);
Set<Entry<String, Integer>> set = map.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
{
public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
{
return (o1.getValue()).compareTo( o2.getValue() );//Ascending order
//return (o2.getValue()).compareTo( o1.getValue() );//Descending order
}
} );
for(Map.Entry<String, Integer> entry:list){
System.out.println(entry.getKey()+" ==== "+entry.getValue());
}
}}
산출
java ==== 20
MAC ==== 26
C++ ==== 45
Unix ==== 67
Why this kolavari ==== 93
주요 문제. 첫 번째 답변을 사용하는 경우 (Google에서 여기로 이동) 등호를 추가하도록 비교기를 변경하십시오. 그렇지 않으면 키로 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
}
이 질문에 대한 답은 이미 많지만, 내가 찾던 것을 제공하지 못했습니다. 키와 항목을 관련 값으로 정렬하여 반환하고 맵에서 키와 값이 수정 될 때이 속성을 유지합니다. 구체적으로 다른 두 가지 질문 이 있습니다.
이 사용 사례를 해결하는 일반적인 예제를 만들었습니다. 이 구현은 값 변경을 반영하고 원래 객체의 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;
}
}
늦은 입장.
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)
당신이 사용하는 것이가? 값이 동일한 경우 키를 비교한다는 것을 알고 있습니다. 정렬이 동일한 값을 가진 요소의 순서를 유지한다는 것을 알았습니다. 키가 이미 정렬 된 경우 키를 기준으로 정렬해야합니까?
주어진지도
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}
상황에 따라 java.util.LinkedHashMap<T>
어떤 항목을 사용하여 항목이 맵에 배치되는지를 기억합니다. 그렇지 않으면 자연 순서에 따라 값을 정렬 해야하는 경우을 통해 정렬 할 수있는 별도의 List를 유지하는 것이 좋습니다 Collections.sort()
.
이후 트리 맵은 <> 작동하지 않는 동일 할 수있는 값에 대해, 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 ,하지만 당신은 단지 바로 반복하는 거라면, 그 여분의 ...
이것은 너무 복잡합니다. 지도는 가치별로 분류하는 등의 일을하지 않아야했습니다. 가장 쉬운 방법은 요구 사항에 맞게 자신 만의 클래스를 만드는 것입니다.
예를 들어 아래에서는 *가있는 곳에 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()
);
}
@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
}
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 );
}
}
이로써 공개 도메인에 기부했습니다.
가장 깨끗한 방법 인 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());
}
}
값이 중복되는 쌍으로 정렬 된 맵을 갖기 위해 몇 가지 간단한 변경 사항이 있습니다. 값이 같은 비교 방법 (클래스 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;
}
}
}
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
그것이 사람들에게 도움이되기를 바랍니다.
중복 키가 있고 작은 데이터 세트 (<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 문에서>를 <로 변경하십시오.
가장 빠른 정렬은 아니지만 추가 종속성없이 작업을 수행합니다.
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
하고Collections.sort ....
그것을 그런 식으로.