¿Por qué EnumMap no es un SortedMap en Java?

9

EnumMap<K extends Enum<K>, V> en Java está claramente ordenado por definición de la enumeración asociada, como también se puede ver en el javadoc:

Los mapas de enumeración se mantienen en el orden natural de sus claves (el orden en que se declaran las constantes de enumeración). Esto se refleja en los iteradores devueltos por las vistas (colecciones keySet(), entrySet()y values()).

Lo que necesito es SortedMapusar una enumeración como tipo de clave. Quiero usar métodos como headMap()o firstKey(), pero quiero sacar provecho del rendimiento agregado de la CPU + memoria de EnumMaps. A TreeMapparece demasiado sobrecarga aquí.

Pregunta : ¿se perdió esto en la implementación, fue pereza (derivado de AbstractMap) o hay una buena razón por la cual EnumMapno es un SortedMap?

código crudo
fuente
¿Qué hay de TreeSet?
Mohammed Deifallah
@MohammedDeifallah Eso es totalmente diferente, no tiene llaves ... ¿Querías decir TreeMap?
deHaar
3
Pude encontrar este problema para openjdk. Es de 2005 pero aún está abierto / sin resolver. Supongo que no hay ninguna "buena razón" para que esto no se implemente.
Amongalen
1
Gracias por las respuestas realmente rápidas, he agregado que encuentro que TreeMap tiene demasiada sobrecarga aquí, más O (log n) para consultas. Pero mi pregunta, por supuesto, también cuenta para EnumSet y SortedSet, el mismo problema.
rawcode
@Amongalen: su respuesta califica como respuesta a mi pregunta, a pesar de que no responde la causa raíz, además de "Oracle no empaña la atención". Ni siquiera Google encontró el problema de OpenJDK que mencionaste, por lo que al menos ayudará a otros con el mismo problema.
rawcode

Respuestas:

3

Esto no responderá a su pregunta principal (porque solo los diseñadores originales tienen la respuesta), pero un enfoque que estaba considerando era que la implementara usted mismo. Mientras trataba de hacer una SortedMapimplementación basada en EnumMap, se me ocurrió la siguiente clase.

Esta es seguramente una implementación rápida y sucia (y tenga en cuenta que no cumple totalmente SortedMap, porque no se cumplen los requisitos de vista ), pero si necesita una, puede mejorarla:

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

Y para una prueba rápida (errores aún por encontrar):

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

Yo obtengo:

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
fuente
1
Comenta que "el mapa devuelto NO es una" vista "o la actual", pero estos métodos (head / sub / tail-Map) DEBEN devolver vistas.
Assylias
1
Veo que ninguna de sus respuestas es realmente una respuesta a mi pregunta principal, pero su respuesta será al menos muy útil para otras personas con este problema, por lo que le daré los créditos por sus esfuerzos. Tal vez alguien en Oracle lo leerá y lo
extraerá
@assylias Así es, y lo mencioné explícitamente en la publicación
ernest_k
Un mapa ordenado que implementa todas esas operaciones, destinado a explotar la naturaleza ordenada, a través de búsquedas lineales ...
Holger
3

Solicitud de función abierta

Pude encontrar este problema para OpenJDK . Es de 2005 pero aún está abierto / sin resolver.

Supongo que no hay ninguna "buena razón" para que esto no se implemente.

Amongalen
fuente
Gracias por hacer esto. Mientras tanto, he visto la respuesta de ernest_k que tampoco responde a mi pregunta en realidad, pero ofrece una buena solución para el problema subyacente a mi pregunta. Lamento no haberle dado los créditos como mencioné primero, pero creo que ernest_k se lo merece por el trabajo.
rawcode
@rawcode Eso es totalmente comprensible. Si yo fuera tú, también aceptaría la respuesta de ernest_k: es mucho más útil que la mía.
Amongalen