Java에서 EnumMap이 SortedMap이 아닌 이유는 무엇입니까?


9

EnumMap<K extends Enum<K>, V> javadoc에서 볼 수 있듯이 Java에서는 관련 열거 형의 정의에 따라 명확하게 정렬됩니다.

열거 형 맵은 키의 자연스러운 순서 (열거 형 상수가 선언 된 순서)로 유지됩니다. 이는 모음 뷰에 의해 반환 된 반복자에 반영된다 ( keySet(), entrySet()values()).

필요한 것은 SortedMap열거 형을 키 유형으로 사용하는 것입니다. headMap()or 와 같은 메소드를 사용 firstKey()하고 싶지만 EnumMaps 의 추가 CPU + 메모리 성능으로 이익을 얻고 싶습니다 . TreeMap너무 많은 오버 헤드 여기 방법 같은 소리.

질문 :이 단지는 (에서 파생 된 게으름되었다 구현에 누락 된 AbstractMap) 또는 왜 좋은 이유가 EnumMap하지 않은 것입니다 SortedMap?


TreeSet는 어떻습니까?
Mohammed Deifallah

@MohammedDeifallah 그것은 완전히 다릅니다. 열쇠가 없습니다 ... 되었습니까 TreeMap?
deHaar

3
openjdk 에서이 문제 를 찾을 수있었습니다 . 2005 년이되었지만 아직 공개 / 해결되지 않았습니다. 나는 이것이 구현되지 않은 "좋은 이유"가 없다고 가정합니다.
사이에

1
정말 빠른 답변에 감사드립니다. 여기서 TreeMap이 너무 많은 오버 헤드와 쿼리에 대한 O (log n)을 발견했습니다. 그러나 내 질문은 물론 동일한 문제 인 EnumSet 및 SortedSet에 대해서도 계산됩니다.
rawcode

@Amongalen : 귀하의 답변은 "Oracle은 포개지지 않습니다"외에도 근본 원인에 대한 답변은 아니지만 내 질문에 대한 답변으로 인정됩니다. 구글조차도 언급 한 OpenJDK 문제를 발견하지 않았으므로 적어도 동일한 문제를 가진 다른 사람들을 도울 것입니다.
rawcode

답변:


3

이것은 원래의 디자이너만이 대답하기 때문에 기본 질문에 대한 답변을하지는 않지만 내가 고려한 한 가지 접근법은 직접 구현하는 것이 었습니다. 에 따라 SortedMap구현을 시도하는 동안 EnumMap다음 클래스를 생각해 냈습니다.

이것은 신속하고 더러운 구현입니다 ( 보기 요구 사항이 충족되지 SortedMap않기 때문에 완전히 준수 하지 않음).하지만 필요한 경우 개선 할 수 있습니다.

class SortedEnumMap<K extends Enum<K>, V> 
    extends EnumMap<K, V> 
    implements SortedMap<K, V> {

    private Class<K> enumClass;
    private K[] values;

    public SortedEnumMap(Class<K> keyType) {
        super(keyType);
        this.values = keyType.getEnumConstants();
        this.enumClass = keyType;

        if (this.values.length == 0) {
            throw new IllegalArgumentException("Empty values");
        }
    }

    @Override
    public Comparator<? super K> comparator() {
        return Comparator.comparingInt(K::ordinal);
    }

    @Override
    public SortedMap<K, V> subMap(K fromKey, K toKey) {
        List<K> keys = Arrays.stream(this.values)
                .dropWhile(k -> k.ordinal() < fromKey.ordinal())
                .takeWhile(k -> k.ordinal() < toKey.ordinal())
                .collect(Collectors.toList());

        return this.forKeys(keys);
    }

    @Override
    public SortedMap<K, V> headMap(K toKey) {
        List<K> keys = new ArrayList<>();

        for (K k : this.values) {
            if (k.ordinal() < toKey.ordinal()) {
                keys.add(k);
            } else {
                break;
            }
        }

        return this.forKeys(keys);
    }

    @Override
    public SortedMap<K, V> tailMap(K fromKey) {
        List<K> keys = new ArrayList<>();

        for (K k : this.values) {
            if (k.ordinal() >= fromKey.ordinal()) {
                keys.add(k);
            }
        }

        return this.forKeys(keys);
    }

    //Returned map is NOT a "view" or the current one
    private SortedEnumMap<K, V> forKeys(List<K> keys) {
        SortedEnumMap<K, V> n = new SortedEnumMap<>(this.enumClass);
        keys.forEach(key -> n.put(key, super.get(key)));

        return n;
    }

    @Override
    public K firstKey() {
        return this.values[0];
    }

    @Override
    public K lastKey() {
        return this.values[this.values.length - 1];
    }
}

그리고 빠른 테스트를 위해 (버그를 아직 찾지 못했습니다) :

SortedMap<Month, Integer> m = new SortedEnumMap(Month.class);

for (Month v : Month.values()) {
    m.put(v, v.getValue());
}

System.out.println("firstKey():       " + m.firstKey());
System.out.println("lastKey():        " + m.lastKey());
System.out.println("headMap/June:     " + m.headMap(Month.JUNE));
System.out.println("tailMap/June:     " + m.tailMap(Month.JUNE));
System.out.println("subMap/April-July " + m.subMap(Month.APRIL, Month.JULY));

나는 얻다:

firstKey():       JANUARY
lastKey():        DECEMBER
headMap/June:     {JANUARY=1, FEBRUARY=2, MARCH=3, APRIL=4, MAY=5}
tailMap/June:     {JUNE=6, JULY=7, AUGUST=8, SEPTEMBER=9, OCTOBER=10, NOVEMBER=11, DECEMBER=12}
subMap/April-July {APRIL=4, MAY=5, JUNE=6}

1
"반환 된 맵은"뷰 "또는 현재 맵이 아니지만 이러한 메소드 (head / sub / tail-Map)는 뷰를 반환해야합니다.
assylias

1
나는 당신의 대답도는 것을 볼 정말 내 기본 질문에 대한 답변,하지만 난 당신의 노력을 위해 당신에게 크레딧을 줄 것이다, 그래서 당신의 대답은,이 문제를 다른 사람에게 적어도 매우 도움이 될 것입니다. 아마 오라클의 누군가가 그것을 읽고 당길 것입니다 ...
rawcode

@assylias 맞습니다, 그리고 나는 그것을 포스트에 명시 적으로 언급했습니다
ernest_k

선형 검색을 통해 정렬 된 특성을 활용하기위한 모든 작업을 구현하는 정렬 된 맵…
Holger

3

공개 기능 요청

나는 발견 할 수 있었다 이 문제를 위해 오픈 JDK . 2005 년이되었지만 아직 공개 / 해결되지 않았습니다.

나는 이것이 구현되지 않은 "좋은 이유"가 없다고 가정합니다.


이렇게 해줘서 고마워 그 동안 나는 실제로 내 질문에 대답하지 않지만 내 질문의 근본 문제에 대한 좋은 해결책을 제시하는 ernest_k의 답변을 보았습니다. 처음 언급 한대로 크레딧을 제공하지 않아서 유감이지만, ernest_k가 해당 작업에 사용할 가치가 있다고 생각합니다.
rawcode

@rawcode 그것은 완전히 이해할 수 있습니다. 내가 너라면 ernest_k의 대답도 받아 들일 것입니다-그것은 내 것보다 훨씬 도움이됩니다.
사이에
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.