Comment inefficace se passe Collections.unmodifiable * une instance qui est déjà enveloppé avec Collections.unmodifiable *?

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

Question

J'ai des morceaux de la pièce se fait par différents cadres sur mesure (code source non disponible) qui instances de dos de la main Carte. Malheureusement, ces cadres ne sont pas cohérents dans leur cas Carte de retour qui ont été emballés avec Collections.unmodifiableMap. Pour assurer une certaine immuabilité (pour faciliter l'utilisation multithread) plus dans mon code, je viens régulièrement appelé Collections.unmodifiableMap sur quoi que ce soit retourné par ces cadres.

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

J'ai réalisé que je pourrais avoir un problème de performance dans cette partie de ma conception. Et que, dans certains cas, je l'appelais Collections.unmodifiableMap faisant passer une instance qui avait déjà été enveloppé par le cadre par le même appel. Et que mon re-emballage a probablement causé un appel de méthode supplémentaire à travers toute l'instance.

Il semble que l'utilisation de « instanceof Collections.UnmodifiableMap » ne fonctionne pas. Et je ne peux pas trouver un moyen de détecter (à l'exception utilisant la réflexion qui ne soit pas une option dans cette situation - trop lent). Si l'instance Carte Je référencement actuellement les besoins à emballer ou non

Questions:

    A) Est-ce que le contrôle de la méthode Collections.unmodifiableMap () pour voir si elle a été adoptée une instance de UnmodifiableMap, et si oui, il suffit de retourner la même référence (évitant ainsi la nécessité de vérifier avant d'appeler la méthode)?
    B) Afin d'éviter de manière proactive des exceptions de modification de réception, est-il un moyen d'interroger une instance carte (autre que par réflexion) pour détecter si elle est mutable (ou immuable)?
    C) Si la réponse à A est non, est-il des gains d'efficacité dans la machine virtuelle Java / HotSpot qui éliminent les frais généraux de l'appel par la méthode multiple du houblon pour se rendre à l'instance de base?
Était-ce utile?

La solution 4

Après avoir examiné tous les commentaires, je suis venu à la conclusion que, peu importe ce que je fais, la solution va être une certaine forme de bidouille (une légère odeur). Je pense que cela est dû au fait que la partie de l'API collections qui produit des instances non modifiables n'a pas fourni pour éviter l'imbrication des instances non modifiables ni ne fournissent un moyen « public » pour un client afin d'éviter bien l'imbrication.

Et en raison de considérations autour de plusieurs chargeurs de classes et de sérialisation via RMI, une solution que j'ai vraiment aimé (comparaison de référence de classe par Jorn Horstmann) a des problèmes. Cependant, quand je prends son approche et le combiner avec une modification de l'approche nom de la classe (recommneded par Eugene Kuleshov), je pense que je reçois aussi près que je vais arriver à avoir une solution qui me aider dans mon multi-thread environnement de traitement distribué. Et il va un peu comme ceci:

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

Cela a encore tous les avantages d'une comparaison de référence en supposant tout se passe dans le même contexte ClassLoader et la chaîne du nom de classe a été correctement interné. Et il le fait tout encapsulation poliment de maintien (en évitant mon code faisant référence au nom de la classe directement). Toutefois, si les deux hypothèses ne tiennent pas, alors l'évaluation retombera à une comparaison de chaînes standard qui fonctionnera en supposant que le nom de la classe ne change pas entre les différentes versions de la bibliothèque (qui semble avoir une probabilité assez faible).

Y at-il que je oublie ou manquant dans cette approche?

Et je vous remercie encore une fois, tout le monde, pour vos commentaires. Je l'apprécie vraiment.

Autres conseils

Au meilleur de ma connaissance:

  • A) Non.
  • B) Non.
  • C) Non.

Goyave les collections 'de Immutable* n'ont pas ce problème. Si ImmutableList.copyOf(list) est appelée avec un list qui lui-même est un ImmutableList, l'argument lui-même est retourné. De plus, vous pouvez les consulter comme (et vérifier avec instanceof pour) le type de Immutable* plutôt que l'interface, ce qui rend facile de savoir si vous avez une instance immuable ou non. Donc, une option est de copier les résultats du cadre dans ces collections immuables et les utiliser tout au long de votre propre code. (Ils ont aussi l'avantage d'être vraiment immuable ... emballages non modifiables permettent l'instance mutable d'origine qu'ils enveloppez être lui-même muté si quelque chose a une référence.)

Cela dit, je ne vous inquiétez pas trop sur les frais généraux possible de passer un appel de méthode par 1 ou 2 couches de l'enveloppe non modifiable, aussi longtemps que vous n'allez pas les envelopper d'une certaine manière encore et encore. Comme d'autres ont en pointe, il est très peu probable que vous aurez remarqué un problème de performance à cause de cela.

Vous n'avez pas à vous soucier de la performance lorsque vous enveloppez une carte non modifiable dans une autre. Jetez un oeil à la classe 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;
}
    ...

Vous pouvez voir que cette classe est seulement une enveloppe mince autour de la carte réelle. Toutes les méthodes comme getSize, isEmpty et d'autres méthodes qui ne sont déléguées à l'instance de carte enveloppées affectent pas l'état de la carte. D'autres méthodes qui affectent l'état de la carte (put, remove) juste jeter UnsupportedOperationException Donc, il est presque nulle surcharge de performance.

Mes pensées sur (C) sont que le compilateur hotspot devrait être assez bon à inline petites méthodes qui renvoient simplement le résultat d'un autre appel de méthode. Mais je doute qu'il puisse voir à travers plusieurs niveaux d'indirection, par exemple un HashMap enveloppé dans plusieurs UnmodifiableMaps. Je pense ne pas non plus que ce serait une optimisation prématurée si vous savez que votre bibliothèque revient parfois non modifiables Maps. Pourquoi ne pas créer votre propre enveloppe pour Collections.unmodifiableMap comme celui-ci (non testé, et je viens de remarquer que simple instanceof ne fonctionnera pas car le Impl dans les collections est privé):

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

B) Non

C) Peut-être, mais je pense que dépendre de la machine virtuelle Java implmentation.

Le décorateur en question est le poids extrêmement léger, est-il une raison spécifique qu'un appel de méthode supplémentaire qui fait simplement un autre appel de méthode causerait des problèmes de performance dans votre environnement? Cela ne devrait pas être un problème dans une application standard, sauf si vous avez une application vraiment spécial / environnement (cpu intensive, en temps réel, appareil mobile ...) il ne devrait pas être un problème.

En général, la première règle est de ne pas optimiser moins qu'il ne soit vraiment nécessaire. Fondamentalement, vous avez besoin de collecter des données sur les performances de votre application pour voir si la collection enveloppée cause de toute baisse de performance.

Mais si vous voulez vraiment vérifier si la collecte est non modifiable, vous pouvez vérifier « UnmodifiableMap » equals (Class.getSimpleName ())

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top