Lo ineficaz está pasando Collections.unmodifiable * una instancia que ya está envuelto con Collections.unmodifiable *?

StackOverflow https://stackoverflow.com/questions/4127921

Pregunta

He bits de trabajo a destajo que están realizando los diversos usos (código fuente no disponible) marcos que respaldan la mano Mapa casos. Por desgracia, estos marcos no son consistentes en sus instancias que regresan mapa que se han envuelto con Collections.unmodifiableMap. Para garantizar un mayor grado de inmutabilidad (para facilitar su uso multi-roscado) en mi código, he llamado simplemente uniformemente Collections.unmodifiableMap en cualquier cosa devuelta por estos marcos.

Map<String, Record> immutableMap = framework.getRecordsByName();
//does this created a nested set of unmodifiableMap wrapper instances?
this.immutableField = Collections.unmodifiableMap(immutableMap);
.
.
.
Map<String, Record> maybeImmutableMap = framework.getRecordsByName();
//is there some means to get instanceof to work?
if (!(maybeImmutableMap instanceof Collections.UnmodifiableMap))
{
    this.immutableField = Collections.unmodifiableMap(maybeImmutableMap);
}

Me di cuenta de que yo podría tener un problema de rendimiento en torno a esta parte de mi diseño. Y que en algunos casos, llamaba Collections.unmodifiableMap pasándole una instancia que ya había sido envuelta por el marco de la misma llamada. Y que mi Reenvasado probablemente causando una llamada a un método adicional a través de toda la instancia.

Parece que el uso de "instanceof Collections.UnmodifiableMap" no funciona. Y no puedo encontrar ninguna manera de detectar (excluyendo el uso de reflexión que no es una opción en esta situación - demasiado lento). Si la instancia Mapa Actualmente estoy Hacer referencia tiene que ajustarse o no

Preguntas:

    A) ¿El Collections.unmodifiableMap () cheque método para ver si se pasa una instancia de UnmodifiableMap, y si es así simplemente devolver la misma referencia (evitando así la necesidad de comprobar antes de llamar al método)?
    B) A fin de evitar de manera proactiva excepciones de modificación de recepción, hay una manera para consultar un ejemplo mapa (con excepción de usar la reflexión) para detectar si es mutable (o inmutable)?
    C) Si la respuesta a A no es, entonces hay algunas eficiencias en la JVM / HotSpot que eliminan la sobrecarga de llamar a través del método múltiples saltos para llegar a la instancia central?
¿Fue útil?

Solución 4

Después de revisar todos los comentarios, que llegó a la conclusión de que no importa lo que haga, la solución va a ser algún tipo de kludge (tienen un olor suave). Creo que esto es debido al hecho de que la parte de la API de colecciones que produce casos no modificables no proporcionó para evitar la anidación casos no modificables ni proporcionaba una manera "pública" para que un cliente correctamente evitar el anidamiento.

Y debido a consideraciones alrededor de múltiples cargadores de clases y serialización a través de RMI, la única solución que me gustó (comparación de referencia de clase por Jorn Horstmann) tiene problemas. Sin embargo, cuando tomo su acercamiento y la combino con una modificación del enfoque nombre de la clase (recommneded por Eugene Kuleshov), creo que llegar lo más cerca que voy a llegar a tener una solución que me ayudará en mi multi-hilo entorno de procesamiento distribuido. Y no hace falta un poco como esto:

public class MyCollections {
    private static final String UNMODIFIABLE_MAP_CLASS_NAME =
        Collections.unmodifiableMap(new HashMap()).getClass().getName();

    public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> map) {
        return map.getClass().getName().equals(UNMODIFIABLE_MAP_CLASS_NAME)
                 ? map
                 : Collections.unmodifiableMap(map);
    }
}

Esto todavía tiene todas las ventajas de una comparación de referencia asumiendo todo lo que está sucediendo dentro del mismo contexto ClassLoader y string del nombre de clase ha sido debidamente internados. Y lo hace mientras cortésmente encapsulación de mantenimiento (evitando mi código hace referencia al nombre de la clase directamente). Sin embargo, si los dos supuestos no se sostienen, entonces la evaluación recurrirá a una comparación de cadenas estándar que funcione asumiendo el nombre de clase no cambia entre las diferentes versiones de la biblioteca (que parece tener una muy baja probabilidad).

¿Hay algo que estoy olvidando o desaparecidos en este enfoque?

Y gracias de nuevo, todo el mundo, por su colaboración. Realmente lo aprecio.

Otros consejos

A lo mejor de mi conocimiento:

  • A) n.
  • B) n.
  • C) No.

guayaba 's colecciones Immutable* no tienen este problema. Si ImmutableList.copyOf(list) se llama con un list que es en sí mismo un ImmutableList, el argumento en sí es devuelto. Además, puede hacer referencia a ellos como (y consulte con instanceof a) el tipo Immutable* en lugar de la interfaz, por lo que es fácil saber si tiene una instancia inmutable o no. Así que una opción es copiar los resultados del marco en estas colecciones inmutables y utilizar los largo de su propio código. (También tienen la ventaja de ser verdaderamente inmutable ... envoltorios no modificables permiten la instancia original, mutable que envuelva a mutar en sí si algo tiene una referencia a él.)

Dicho todo esto, yo no preocuparse demasiado acerca de la posible sobrecarga de pasar a través de una llamada al método 1 o 2 capas de envoltura no modificables, siempre y cuando usted no va a envolver alguna manera una y otra vez. Como otros han apuntado a cabo, es muy poco probable que se le ha dado cuenta de un problema de rendimiento debido a esto.

Usted no tiene que preocuparse por el rendimiento cuando se envuelve un mapa no se puede modificar en otra. Echar un vistazo a la clase UnmodifiableMap:

private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
    ...
UnmodifiableMap(Map<? extends K, ? extends V> m) {
        if (m==null)
            throw new NullPointerException();
        this.m = m;
    }

public int size()                {return m.size();}
    ...
public V put(K key, V value) {
    throw new UnsupportedOperationException();
    }
public V remove(Object key) {
    throw new UnsupportedOperationException();
    }

public Set<K> keySet() {
    if (keySet==null)
    keySet = unmodifiableSet(m.keySet());
    return keySet;
}

public Set<Map.Entry<K,V>> entrySet() {
    if (entrySet==null)
    entrySet = new UnmodifiableEntrySet<K,V>(m.entrySet());
    return entrySet;
}
    ...

Se puede ver que esta clase es sólo una envoltura delgada alrededor del mapa real. Todos los métodos como getSize, isEmpty y otros métodos que no afectan el estado del mapa se delegan a la instancia mapa envueltos. Otros métodos que afectan de mapa del estado (put, remove) acaba de lanzar UnsupportedOperationException Así que no hay casi cero sobrecarga de rendimiento.

Mis pensamientos sobre (C) son que el compilador de punto de acceso debe ser bastante bueno en pequeñas inlining métodos que sólo devuelve el resultado de otra llamada al método. Pero dudo que se pueda ver a través de varios niveles de indirección, por ejemplo un HashMap envuelto en varios UnmodifiableMaps. Asimismo, no creo que sería la optimización prematura si sabe que su biblioteca se vuelve a veces no modificables Maps. ¿Por qué no crear su propia envoltura para Collections.unmodifiableMap como esto (no probado, y me he dado cuenta de que una sencilla instanceof no funcionará ya que la Impl en las colecciones es privado):

public class MyCollections {
    private static final Class UNMODIFIABLE_MAP_CLASS = Collections.unmodifiableMap(new HashMap()).getClass();

    public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> map) {
        return map.getClass() == UNMODIFIABLE_MAP_CLASS ? map : Collections.unmodifiableMap(map);
    }
}

A) No

B) No

C) Es posible, pero yo esperaría que depender de la implmentation JVM.

El decorador en cuestión es extremadamente ligero, ¿hay alguna razón específica de que una llamada a un método adicional que simplemente hace otra llamada al método podría causar algunos problemas de rendimiento en su entorno? Esto no debería ser un problema en cualquier aplicación estándar, por lo menos que tenga alguna aplicación / Medio ambiente realmente especial (CPU, en tiempo real, un dispositivo móvil ...) no debería ser un problema.

En general, la primera regla es no optimizarlo a menos que sea realmente necesario. Básicamente es necesario recoger algunos datos de rendimiento de su aplicación para ver si está envuelto colección causa ningún impacto en el rendimiento.

Pero si realmente quiere comprobar si la recolección es inmodificable, se puede comprobar .equals "UnmodifiableMap" (Class.getSimpleName ())

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top