Por que o EnumMap não é um SortedMap em Java?

9

EnumMap<K extends Enum<K>, V> em Java é claramente ordenado por definição da enum associada, como você também pode ver no javadoc:

Os mapas enum são mantidos na ordem natural de suas chaves (a ordem na qual as constantes enum são declaradas). Isto reflecte-se nas iterators retornados pelas vistas colecções ( keySet(), entrySet(), e values()).

O que eu preciso é SortedMapusar uma enumeração como tipo de chave. Eu quero usar métodos como headMap()ou firstKey(), mas quero aproveitar o desempenho adicionado da memória cpu + de EnumMaps. A TreeMapsons como forma demasiada sobrecarga aqui.

Pergunta : isso foi esquecido na implementação, foi preguiça (derivada de AbstractMap) ou existe um bom motivo para EnumMapnão ser um SortedMap?

código bruto
fonte
E o TreeSet?
Mohammed Deifallah 15/01
@MohammedDeifallah Isso é totalmente diferente, não tem chaves ... Você quis dizer TreeMap?
deHaar 15/01
3
Consegui encontrar esse problema no openjdk. É a partir de 2005, mas ainda está aberto / não resolvido. Eu diria que não há nenhuma "boa razão" para que isso não seja implementado.
Amongalen
11
Obrigado pelas respostas realmente rápidas, acrescentei que acho o TreeMap muito caro aqui, além de O (log n) para consultas. Mas, claro, minha pergunta também conta para EnumSet e SortedSet, o mesmo problema.
rawcode 15/01
@ Amongalen: sua resposta é qualificada como resposta à minha pergunta, mesmo que não responda à causa raiz, além de "A Oracle não se preocupa". Nem o google encontrou o problema do OpenJDK que você mencionou, pelo menos isso ajudará outras pessoas com o mesmo problema.
rawcode 15/01

Respostas:

3

Isso não responderá à sua pergunta principal (porque apenas os designers originais têm a resposta), mas uma abordagem que eu estava pensando era que você a implementasse por conta própria. Enquanto tentava fazer uma SortedMapimplementação baseada EnumMap, criei a seguinte classe.

Esta é certamente uma implementação rápida e suja (e observe que ela não é totalmente compatível SortedMap- porque os requisitos de exibição não são atendidos), mas se você precisar de uma, poderá melhorá-la:

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

E para um teste rápido (erros ainda a serem encontrados):

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

Eu recebo:

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}
ernest_k
fonte
11
Você comenta que "o mapa retornado NÃO é uma" visualização "ou o atual", mas esses métodos (mapa de cabeça / sub / cauda) DEVEM retornar visualizações.
assylias 15/01
11
Vejo que nenhuma das suas respostas é realmente uma resposta à minha pergunta principal, mas sua resposta será pelo menos muito útil para outras pessoas com esse problema, por isso darei os créditos por seus esforços. Talvez alguém da Oracle leia e puxe ...
rawcode
@assylias Isso mesmo, e eu mencionei explicitamente isso no post
ernest_k 15/01
Um mapa classificado que implementa todas essas operações, destinado a explorar a natureza classificada, através de pesquisas lineares ...
Holger
3

Solicitação de recurso aberto

Consegui encontrar esse problema no OpenJDK . É a partir de 2005, mas ainda está aberto / não resolvido.

Eu diria que não há nenhuma "boa razão" para que isso não seja implementado.

Amongalen
fonte
obrigado por fazer isso. Enquanto isso, tenho visto a resposta de ernest_k, que também não responde à minha pergunta, mas traz uma boa solução para o problema subjacente à minha pergunta. Me desculpe, eu não lhe dei os créditos como mencionei primeiro, mas acho que o ernest_k merece isso pelo trabalho.
rawcode 15/01
@rawcode Isso é totalmente compreensível. Se eu fosse você, também aceitaria a resposta de ernest_k - é muito mais útil que a minha.
Amongalen