저는 Java에 비교적 익숙하지 않습니다.Map<Key, Value>
값에.
값이 고유하지 않기 때문에 나는keySet
~로array
, 그리고 그 배열을 통해 정렬배열 정렬~와사용자 지정 비교기키와 관련된 값을 정렬합니다.
더 쉬운 방법이 있습니까?
다음은 일반적인 친화적 인 버전입니다.
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
대신에forEach
, 이후의 문서forEach
상태 : "이 연산의 동작은 명시 적으로 비 결정적입니다." - rob
이 코드는 여러 가지 방법으로 중단 될 수 있습니다.제공된 코드를 사용하려는 경우 해당 의미를 숙지하기 위해 주석을 읽으십시오. 예를 들어 값을 더 이상 키로 검색 할 수 없습니다. (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)))
? - Stephenmap.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
- steffen
자바 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
세 줄의 답이 세 개 있습니다 ...
나는Google 소식 모음
구아바이 작업을 수행하려면 - 값이Comparable
그럼 당신은 사용할 수 있습니다.
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
그러면지도에 대한 함수 (객체)가 만들어지고 [입력 값으로 키를 가져 와서 해당 값을 반환하는], 자연스러운 (비교 가능한) 순서 [값]이 적용됩니다.
만약 그들이 비교할 수 없다면, 당신은 라인을 따라 뭔가를 할 필요가 있습니다.
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
이것들은 TreeMap에 적용 할 수 있습니다 (Ordering
확장하다Comparator
), 또는정렬 후 LinkedHashMap
주의: TreeMap을 사용하려는 경우, 비교 == 0이면 항목이 이미 목록에 있음을 기억하십시오 (동일한 값을 여러 값으로 가질 경우 발생합니다). 이것을 줄이기 위해 비교기에 키를 추가 할 수 있습니다 (키와 값이Comparable
) :
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
=키에 의해 매핑 된 값에 자연 순서를 적용하고 키의 자연 순서에 따라 자연 순서를 적용합니다.
키를 0과 비교하면 여전히 작동하지 않지만 대부분의 경우 키가 충분해야합니다.comparable
항목 (hashCode
,equals
과compareTo
자주 동기화됩니다 ...)
만나다Ordering.onResultOf ()과Functions.forMap ().
이제 우리가 원하는 것을 수행하는 비교자를 얻었으므로 결과를 얻어야합니다.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
이제 이것이 가장 효과가있을 것입니다.
TreeMap
; 삽입 된 키가 풋 이후까지 값을 가지지 않을 때를 비교하려고 시도 할 필요가 없습니다. 즉, 굉장히 빠를 것입니다.포인트 1은 나를위한 약간의 거래 차단기이다; Google 콜렉션은 믿을 수 없을만큼 게으른 것입니다 (좋은 점이 있습니다 : 즉석에서 모든 작업을 수행 할 수 있으며 결과를 사용할 때 실제 작업이 완료 됨).완전한지도!
그래도 걱정하지 마십시오. 이런 식으로 정렬 된 "라이브"맵을 가지고 사로 잡히면 위의 문제 중 하나만 아니라 둘 다를 해결할 수 있습니다 (!)
참고 : 이것은 2012 년 6 월에 크게 바뀌 었습니다. 이전 코드는 작동하지 않을 수 있습니다. 내부 HashMap은 코드 사이에 무한 루프를 만들지 않고 값을 조회해야합니다.TreeMap.get()
->compare()
과compare()
->get()
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
//A map for doing lookups on the keys for comparison so we don't get infinite loops
private final Map<K, V> valueMap;
ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
this(partialValueOrdering, new HashMap<K,V>());
}
private ValueComparableMap(Ordering<? super V> partialValueOrdering,
HashMap<K, V> valueMap) {
super(partialValueOrdering //Apply the value ordering
.onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
.compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
this.valueMap = valueMap;
}
public V put(K k, V v) {
if (valueMap.containsKey(k)){
//remove the key in the sorted set before adding the key again
remove(k);
}
valueMap.put(k,v); //To get "real" unsorted values for the comparator
return super.put(k, v); //Put it in value order
}
public static void main(String[] args){
TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
map.put("a", 5);
map.put("b", 1);
map.put("c", 3);
assertEquals("b",map.firstKey());
assertEquals("a",map.lastKey());
map.put("d",0);
assertEquals("d",map.firstKey());
//ensure it's still a map (by overwriting a key, but with a new value)
map.put("d", 2);
assertEquals("b", map.firstKey());
//Ensure multiple values do not clobber keys
map.put("e", 2);
assertEquals(5, map.size());
assertEquals(2, (int) map.get("e"));
assertEquals(2, (int) map.get("d"));
}
}
넣으면 해시 맵에 비교 자의 값이 있는지 확인한 다음 정렬을 위해 TreeSet에 놓습니다. 하지만 그 전에는 키가 실제로 중복되지 않았 음을 확인하기 위해 해시 맵을 확인합니다. 또한 우리가 생성하는 콤퍼레이터에는 키가 포함되어 있으므로 중복 된 값이 비 중복 키를 삭제하지 않습니다 (== 비교로 인해).
이 2 개의 항목은치명적인지도 계약을 지키기위한 것; 네가 그렇게 생각하지 않는다면지도를 완전히 뒤집을 시점에있다.Map<V,K>
).
생성자는 다음과 같이 호출되어야합니다.
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
Ordering
단순히 부유하다.Comparator
. 각 예제 (각 밑의 기울임 꼴)에 주석을 달았습니다. "천연" 객체가Comparable
; 그것은 apache common의 ComparableComparator와 같습니다.onResultOf
비교할 항목에 함수를 적용합니다. 정수에 1을 더한 함수가 있다면natural().onResultOf(add1Function).compare(1,2)
끝낼거야.2.compareTo(3)
- StephenImmutableSetMultiMap
또는ImmutableListMultiMap
중복 변수의 모음을 포함합니다. - Stephen
에서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))
- Vlad HolubievEntry.comparingByValue(Comparator.reverseOrder())
- Gediminas Rimsa
키를 정렬하려면 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();
마지막으로 정렬 된 정보에 계속 액세스해야하는 경우 (한 번만 정렬하는 것보다) 추가 멀티 맵을 사용할 수 있습니다. 더 자세한 정보가 필요하면 알려주세요.
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());
}
엔트리는, 지정된 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 될 때마다 주어진 맵의 새로운 스냅 샷을 생성하므로 동시 변경을 막을 때 항상 맵의 현재 상태를 반영합니다.
지도를 정렬해야한다는 끊임없는 필요성은 아마도 냄새라고 생각하지만 다음 코드는 다른 데이터 구조를 사용하지 않고도이를 수행하는 가장 쉬운 방법이라고 생각합니다.
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}
귀하가 동등한 2 개의 항목을 가지고있을 때 가장 많이 투표 한 답변은 효과가 없습니다. TreeMap은 동일한 값을 유지합니다.
예 : 정렬되지 않은지도
key/value: D/67.3 key/value: A/99.5 key/value: B/67.4 key/value: C/67.5 key/value: E/99.5
결과들
key/value: A/99.5 key/value: C/67.5 key/value: B/67.4 key/value: D/67.3
그래서 밖으로 E!
나를 위해 괜찮아요 0, -1 반환하지 않는 경우 equals 비교기를 조정하려면 작동합니다.
예제에서 :
ValueComparator 클래스는 Comparator {
지도 기본; public ValueComparator (Map base) { this.base = base; }
공공 int compare (Object a, Object b) {
if((Double)base.get(a) < (Double)base.get(b)) { return 1; } else if((Double)base.get(a) == (Double)base.get(b)) { return -1; } else { return -1; }
} }
이제 반환 :
정렬되지 않은지도 :
key/value: D/67.3 key/value: A/99.5 key/value: B/67.4 key/value: C/67.5 key/value: E/99.5
결과 :
key/value: A/99.5 key/value: E/99.5 key/value: C/67.5 key/value: B/67.4 key/value: D/67.3
외계인에 대한 응답으로 (2011 년 22 월 22 일) : 나는이 솔루션을 Integer ID와 이름의 맵에 사용하고 있지만 생각은 동일하므로 위의 코드가 올바르지 않을 수도 있습니다 (테스트에서 작성하고 올바른 코드를 제공합니다). 이것이 코드입니다. 위의 솔루션을 기반으로지도 정렬 :
package nl.iamit.util;
import java.util.Comparator;
import java.util.Map;
public class Comparators {
public static class MapIntegerStringComparator implements Comparator {
Map<Integer, String> base;
public MapIntegerStringComparator(Map<Integer, String> base) {
this.base = base;
}
public int compare(Object a, Object b) {
int compare = ((String) base.get(a))
.compareTo((String) base.get(b));
if (compare == 0) {
return -1;
}
return compare;
}
}
}
그리고 이것은 테스트 클래스입니다 (방금 테스트 해 보았습니다. Integer, String Map에서 작동합니다 :
package test.nl.iamit.util;
import java.util.HashMap;
import java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class TestComparators {
@Test
public void testMapIntegerStringComparator(){
HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
unSoretedMap);
TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
//the testdata:
unSoretedMap.put(new Integer(1), "E");
unSoretedMap.put(new Integer(2), "A");
unSoretedMap.put(new Integer(3), "E");
unSoretedMap.put(new Integer(4), "B");
unSoretedMap.put(new Integer(5), "F");
sorted_map.putAll(unSoretedMap);
Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
Object[] currecntKeys=sorted_map.keySet().toArray();
assertArrayEquals(targetKeys,currecntKeys);
}
}
다음은지도의 Comparator 코드입니다.
public static class MapStringDoubleComparator implements Comparator {
Map<String, Double> base;
public MapStringDoubleComparator(Map<String, Double> base) {
this.base = base;
}
//note if you want decending in stead of ascending, turn around 1 and -1
public int compare(Object a, Object b) {
if ((Double) base.get(a) == (Double) base.get(b)) {
return 0;
} else if((Double) base.get(a) < (Double) base.get(b)) {
return -1;
}else{
return 1;
}
}
}
이것은 이것을위한 테스트 케이스입니다 :
@Test
public void testMapStringDoubleComparator(){
HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
unSoretedMap);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
//the testdata:
unSoretedMap.put("D",new Double(67.3));
unSoretedMap.put("A",new Double(99.5));
unSoretedMap.put("B",new Double(67.4));
unSoretedMap.put("C",new Double(67.5));
unSoretedMap.put("E",new Double(99.5));
sorted_map.putAll(unSoretedMap);
Object[] targetKeys={"D","B","C","E","A"};
Object[] currecntKeys=sorted_map.keySet().toArray();
assertArrayEquals(targetKeys,currecntKeys);
}
당신은 이것을 훨씬 더 일반적으로 만들 수 있지만, 나는 단지 1 케이스 (지도)
다음과 같은 일반 비교기를 사용하십시오.
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));
}
}
사용하는 대신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;
}
이것은 앤서니 (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을 반환한다는 것입니다.
주요 문제. 첫 번째 대답을 사용하면 (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
}
최선의 접근법
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
이미이 질문에 대한 답변이 많이 있지만, 내가 원하는 것을 제공하지 못했습니다. 키와 항목을 연관된 값으로 정렬하여 반환하는지도 구현이며 키와 값이지도에서 수정 될 때이 속성을 유지 관리합니다. 두다른 질문들이것을 구체적으로 요구하십시오.
이 사용 사례를 해결하는 일반적인 친숙한 예제를 만들었습니다. 이 구현은 Map 인터페이스의 모든 계약 (예 : keySet () 및 setSet ()에서 반환 된 값 변경 및 제거를 원래 객체로 반영하는 것과 같은)을 준수하지 않습니다. 스택 오버플로 (Stack Overflow) 답변에 포함하기에는 너무 큰 해결책이라고 느꼈습니다. 좀 더 완벽한 구현을 만들 수 있다면 아마도 Github에 게시 한 다음이 답변의 업데이트 된 버전에 링크 할 것입니다.
import java.util.*;
/**
* A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
* by associated values based on the the comparator provided at construction
* time. The order of two or more keys with identical values is not defined.
* <p>
* Several contracts of the Map interface are not satisfied by this minimal
* implementation.
*/
public class ValueSortedMap<K, V> extends HashMap<K, V> {
protected Map<V, Collection<K>> valueToKeysMap;
// uses natural order of value object, if any
public ValueSortedMap() {
this((Comparator<? super V>) null);
}
public ValueSortedMap(Comparator<? super V> valueComparator) {
this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
}
public boolean containsValue(Object o) {
return valueToKeysMap.containsKey(o);
}
public V put(K k, V v) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
valueToKeysMap.get(oldV).remove(k);
}
super.put(k, v);
if (!valueToKeysMap.containsKey(v)) {
Collection<K> keys = new ArrayList<K>();
keys.add(k);
valueToKeysMap.put(v, keys);
} else {
valueToKeysMap.get(v).add(k);
}
return oldV;
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public V remove(Object k) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
super.remove(k);
valueToKeysMap.get(oldV).remove(k);
}
return oldV;
}
public void clear() {
super.clear();
valueToKeysMap.clear();
}
public Set<K> keySet() {
LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
for (V v : valueToKeysMap.keySet()) {
Collection<K> keys = valueToKeysMap.get(v);
ret.addAll(keys);
}
return ret;
}
public Set<Map.Entry<K, V>> entrySet() {
LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
for (Collection<K> keys : valueToKeysMap.values()) {
for (final K k : keys) {
final V v = get(k);
ret.add(new Map.Entry<K,V>() {
public K getKey() {
return k;
}
public V getValue() {
return v;
}
public V setValue(V v) {
throw new UnsupportedOperationException();
}
});
}
}
return ret;
}
}
컨텍스트에 따라java.util.LinkedHashMap<T>
항목이지도에 배치되는 순서를 기억합니다. 그렇지 않으면 자연 순서에 따라 값을 정렬해야하는 경우에는 다음을 통해 정렬 할 수있는 별도의 목록을 유지하는 것이 좋습니다.Collections.sort()
.
이후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;
}
넣고 싶을지도 모릅니다.명부안에연결된 해시 맵, 그러나 만약 당신이 단지 그것을 반복해서 반복한다면, 그것은 불필요한 것입니다 ...
이것은 너무 복잡합니다. 지도는 가치로 분류하는 것과 같은 일을하지 않아야했습니다. 가장 쉬운 방법은 요구 사항에 맞는 맞춤 클래스를 만드는 것입니다.
아래 예제에서 *가있는 곳에 비교기 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()
);
}
@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 문에서.
가장 빠른 정렬이 아니지만 추가 종속성없이 작업을 수행합니다.
user157196과 Carter Page의 솔루션을 병합했습니다.
class MapUtil {
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue( Map<K, V> map ){
ValueComparator<K,V> bvc = new ValueComparator<K,V>(map);
TreeMap<K,V> sorted_map = new TreeMap<K,V>(bvc);
sorted_map.putAll(map);
return sorted_map;
}
}
class ValueComparator<K, V extends Comparable<? super V>> implements Comparator<K> {
Map<K, V> base;
public ValueComparator(Map<K, V> base) {
this.base = base;
}
public int compare(K a, K b) {
int result = (base.get(a).compareTo(base.get(b)));
if (result == 0) result=1;
// returning 0 would merge keys
return result;
}
}
Guava의 멀티 맵을 사용해보십시오.
TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
Multimaps.invertFrom(Multimaps.forMap(originalMap),
ArrayListMultimap.<Integer, String>create()).asMap());
결과적으로 원래 값에서 해당 값에 해당하는 키 모음으로 맵을 가져옵니다. 동일한 값에 대해 여러 개의 키가있는 경우에도이 방법을 사용할 수 있습니다.
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
과Collections.sort ....
그런 식으로. - Hannes