2010-01-15 14 views
6

Está estático inicializado inmodificableCollection.get garantizado inmutable?Está estático inicializado inmodificableCollection.get garantizado inmutable?

Para:

final de FOO mapa estático = Collections.unmodifiableMap (nuevo HashMap());

¿Pueden los subprocesos múltiples usar el método get y no tener problemas?

Incluso los elementos en FOO no se pueden agregar ni eliminar, lo que impide que el método get manipule el estado interno de FOO con fines de caché, etc. Si el estado interno se modifica de alguna manera, FOO no se puede usar al mismo tiempo. Si este es el caso, ¿dónde están las verdaderas colecciones inmutables en java?

+1

En realidad es una buena pregunta. Piensa en 'WeakHashMap', eso puede cambiar sin cambiar. 'LinkedHashMap' en el modo de orden de acceso es el mismo. –

+0

(Es posible que desee copiar el título de su pregunta en la pregunta: ceguera de banner.) –

+0

El autor tiene una buena pregunta. Digamos que escribo mi propia implementación de mapas. Supongamos que en cada búsqueda, reorganiza su estructura interna para que los elementos más buscados se encuentren más rápido la próxima vez. El usuario envuelve dicho mapa con una colección no modificable y asume que es seguro para los hilos. Luego, 2 subprocesos acceden a diferentes valores simultáneamente y el optimizador interno daña el estado interno del mapa. – z5h

Respuesta

-1

Sugeriría cualquier operación de subprocesamiento para usar ConcurrentHashMap o HashTable, ambos son seguros para subprocesos.

+0

Alguien está en una juerga de voto, lol. –

+1

Sí, usar operaciones de simultaneidad para datos inmutables es probablemente una mala idea. –

0

No hay un mapa inmutable verdadero en Java SDK. Todos los mapas sugeridos por Chris solo son seguros para subprocesos. El Mapa no modificable tampoco es inmutable, ya que si el Mapa subyacente cambió también habrá ConcurrentModificationException.

Si desea el mapa verdaderamente inmutable, utilice ImmutableMap de Google Collections/Guava.

-1

Si un getter en el mapa devuelto se mueve con algún estado interno no es importante, siempre y cuando el objeto cumpla con su contrato (que es un mapa que no se puede modificar). Entonces tu pregunta es "ladrar el árbol equivocado".

Tiene razón al ser cauteloso con UnmodifiableMap, en el caso de que no tenga la propiedad y el control sobre el mapa que envuelve. Por ejemplo

Map<String,String> wrapped = new HashMap<String,String>(); 
wrapped.add("pig","oink"); 
Map<String,String> wrapper = Collections.unmodifiableMap(wrapped); 
System.out.println(wrapper.size()); 
wrapper.put("cow", "moo"); // throws exception 
wrapped.put("cow", "moo"); 
System.out.println(wrapper.size()); // d'oh! 
+0

"sin importancia" - ¡no es cierto! ¡Es muy importante si la clase no es segura para subprocesos y dos subprocesos se cruzan uno contra el otro! –

+0

Léelo de nuevo, señor. "Siempre que respete su contrato". –

0

En realidad, es una buena pregunta. Piense en WeakHashMap, que puede cambiar sin que se le solicite una operación de mutación. LinkedHashMap en el modo de orden de acceso es muy similar.

Los documentos de la API para HashMap estado:

Tenga en cuenta que esta aplicación no es sincronizada. Si hay varios subprocesos que acceden simultáneamente a un mapa hash y en al menos uno de los subprocesos modifica estructuralmente el mapa , debe estar sincronizado externamente. (A modificación estructural es cualquier operación que agrega o elimina uno o más asignaciones; cambiando simplemente el valor asociado con una clave que una instancia ya contiene no es una modificación estructural .)

Presumiblemente que debería ser si y solo si. Eso significa que get no necesita sincronizarse si el HashMap es "efectivamente inmutable".

+0

Mr/Ms Downvoter: ¿Hay algún problema con mi respuesta o solo con la venganza? –

4

dado el ejemplo específico:

static final Map FOO = Collections.unmodifiableMap(new HashMap()); 

Entonces FOO habrá inmutable. Tampoco tendrá ningún elemento.Dado el caso más general de:

static final Map BAR = Collections.unmodifiableMap(getMap()); 

Entonces si es o no es inmutable es totalmente dependiente de si o no a otra persona puede llegar al mapa subyacente, y qué tipo de mapa que es. Por ejemplo, si se trata de un LinkedHashMap, la lista vinculada subyacente podría modificarse por orden de acceso y podría cambiar llamando a get(). La forma más segura (utilizando clases no concurrentes) para hacer esto sería:

static final Map BAR = Collections.unmodifiableMap(new HashMap(getMap())); 

la javadocs for HashMap implica que, mientras no efectúe cambios estructurales en el mapa, entonces es seguro de usar en forma concurrente, por lo que este debería ser seguro para cualquiera de los accesores que pueda usar, es decir, obtener los diversos conjuntos e iterar sobre ellos y get() debería ser seguro.

Si puede utilizar las clases concurrentes, entonces también podría hacer:

static final Map BAR = Collections.unmodifiableMap(new ConcurrentHashMap(getMap()); 

Ésta será explícitamente seguro de usar desde varios subprocesos, ya que es ConcurrentHashMap explícitamente el acceso de múltiples subprocesos. El estado interno puede ser mutable, pero el estado visible externamente no lo será, y dado que se garantiza que la clase será segura para hilos, podemos considerar que es externamente inmutable.

+0

¿Qué ocurre si, por ejemplo, quiero iterar sobre 'BAR'? –

+0

@Tom: Como mencionas en tu respuesta, y ahora lo menciono en el mío ;-), iterar sobre el mapa debe ser tan seguro como usar get. –

+0

Pero está hablando de 'ConcurrentHashMap' y' Map's de implementación no especificada. –

2

A riesgo de sonar como si estuviera en una juerga publicitaria, use el Google Immutable Collections y termine con eso.

+0

¿Por qué introduciría una dependencia así sin motivo? –

+0

¿Por qué saltar obstáculos para asegurar la inmutabilidad cuando alguien más ya lo ha hecho? – Carl

+3

Tom, algunas personas simplemente tienen una preferencia estilística para 'mapa final estático público MAP = ImmutableMap.of (foo1, bar1, foo2, bar2); 'over' public static Map final MAP = initMap(); privado static Map initMap() {Map map = new HashMap (); map.put (foo1, bar1); map.put (foo2, bar2); return Collections.unmodifiableMap (mapa); } ' –

Cuestiones relacionadas