web-dev-qa-db-fra.com

Que fait Collections.unmodifiableSet () en Java?

Je peux voir que Collections.unmodifiableSet renvoie une vue non modifiable de l'ensemble donné, mais je ne comprends pas pourquoi nous ne pouvons pas simplement utiliser le modificateur final pour accomplir cela.

Dans ma compréhension, final déclare une constante: quelque chose qui ne peut pas être modifié. Ainsi, si un ensemble est déclaré en tant que constante, il ne peut pas être modifié: rien ne peut être supprimé de l'ensemble et rien ne peut être ajouté.

Pourquoi avons-nous besoin de Collections.unmodifiableSet?

29
Roman

final déclare une référence d'objet qui ne peut pas être modifiée, par exemple.

private final Foo something = new Foo();

crée une nouvelle Foo et place la référence dans something. Par la suite, il n'est pas possible de modifier something pour qu'il pointe vers une instance différente de Foo.

Cela empêche pas de modifier l’état interne de l’objet. Je peux toujours appeler toutes les méthodes de Foo accessibles au périmètre concerné. Si une ou plusieurs de ces méthodes modifient l'état interne de cet objet, alors final ne l'empêchera pas.

À ce titre, les éléments suivants:

private final Set<String> fixed = new HashSet<String>();

est-ce que not crée une Set qui ne peut pas être ajouté ou autrement modifié; cela signifie simplement que fixed ne fera jamais référence à cette instance.

En revanche, en faisant:

private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );

crée une instance de Set qui lancera UnsupportedOperationException si on tente d'appeler fixed.add() ou fixed.remove(), par exemple - l'objet lui-même protégera son état interne et l'empêchera d'être modifié.

Par souci d'exhaustivité:

private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );

crée une instance de Set qui n'autorisera pas la modification de son état interne et signifie également que fixed ne pointe jamais que vers une instance de cet ensemble.

La raison pour laquelle final peut être utilisé pour créer des constantes de primitives est basée sur le fait que la valeur ne peut pas être modifiée. Rappelez-vous que fixed ci-dessus n'était qu'une référence - une variable contenant une adresse qui ne peut pas être modifiée. Eh bien, pour les primitives, par exemple.

private final int ANSWER = 42;

la valeur de ANSWER est celle de 42. Étant donné que ANSWER ne peut pas être modifié, sa valeur sera toujours 42.

Voici un exemple qui brouille toutes les lignes:

private final String QUESTION = "The ultimate question";

Selon les règles ci-dessus, QUESTION contient l'adresse d'une instance de String qui représente "la question ultime" et cette adresse ne peut pas être modifiée. La chose à retenir ici est que String est lui-même immuable - vous ne pouvez rien faire à une instance de String qui le modifie, et toute opération qui le ferait autrement (telle que replace, substring, etc.) renvoie des références à instances de String.

59
Rob

final garantit uniquement que la référence de l'objet représenté par la variable ne peut pas être modifiée; il ne fait rien pour l'instance de l'objet et sa mutabilité. 

final Set s = new Set(); garantit simplement que vous ne pourrez plus faire s = new Set();. Cela ne rend pas le jeu non modifiable, mais si vous ne pouviez rien y ajouter pour commencer. Donc, pour que ce soit vraiment clair, final n'affecte que la variable reference pas l'objet vers lequel la référence pointe.

Je peux faire ce qui suit:

final List<String> l = new ArrayList<String>();
l.add("hello");
l.add("world");
l.remove(0);

mais je ne peux pas faire ça.

l = new ArrayList<String>();

encore une fois à cause de la variable final, je ne peux pas modifier le sens de la variable l.

vous devez effectuer l'une des trois opérations suivantes pour sécuriser un thread de conteneur Collection.

Java.util.Collections.syncronizedXXX();

ou

Java.util.Collections.unmodifiableXXX();

ou utilisez l'un des conteneurs appropriés à partir de Java.util.concurrency.* package.

si j'avais un objet Person et que j'avais final Person p = new Person("me");, cela signifie que je ne peux pas réaffecter p pour qu'il pointe vers un autre objet Person. Je peux encore faire p.setFirstName("you"); 

Ce qui confond la situation est que 

final int PI = 3.14;
final String greeting = "Hello World!";

ressemblent à const en C++, alors que les objets qu’ils désignent sont immuables/non modifiables par nature. Les conteneurs ou les objets utilisant des méthodes de mutateur pouvant modifier l'état interne de l'objet ne sont pas const, mais uniquement les référence de ces objets sont final et ne peuvent pas être réaffectés à reference un autre objet.

15
user177800

final n'est pas (style C++) const. Contrairement à C++, Java n'a pas de méthode const- ou quoi que ce soit du genre, et les méthodes qui peuvent modifier l'objet peuvent être appelées via une référence final.

Collections.unmodifiable* est un wrapper qui applique (au moment de l'exécution, pas au moment de la compilation) la lecture seule pour la collection concernée.

3
Chris Jester-Young

La Collections.unmodifiableSet(Set<? extends T>) créera un wrapper sur le jeu d'origine. Cet ensemble de wrapper ne peut pas être modifié. mais le jeu d'origine peut toujours être modifié.

Exemple:

 Set<String> actualSet=new HashSet<String>(); //Creating set

Ajout de quelques éléments

actualSet.add("aaa");
actualSet.add("bbb");

Impression d'éléments ajoutés

System.out.println(actualSet);   //[aaa, bbb]

Placez la actualSet dans un ensemble non modifiable et attribuez-la à une nouvelle référence (wrapperSet).

Set<String> wrapperSet=Collections.unmodifiableSet(orginalSet);

Imprimez le wrapperSet. donc il aura actualSet valeurs

System.out.println(wrapperSet);   //[aaa, bbb]

essayons de supprimer/ajouter un élément sur wrapperSet.

wrapperSet.remove("aaa");   //UnSupportedOperationException 

Ajouter un élément de plus dans actualSet

    actualSet .add("ccc");

Imprimer actualSet et wrapperSet. les deux valeurs sont identiques. donc, si vous ajoutez/supprimez des éléments dans le jeu réel, les modifications seront également reflétées dans le jeu de wrapper.

    System.out.println(actualSet);  //[aaa, ccc, bbb]
    System.out.println(wrapperSet);  // [aaa, ccc, bbb]

Utilisation:

Cette Collections.unmodifiableSet(Set<? extends T>) est utilisée pour empêcher la modification de la méthode getter de Set pour tout objet. disons

public class Department{

    private Set<User> users=new HashSet<User>();

    public Set<User> getUsers(){
        return Collections.unmodifiableSet(users); 
    }
}
2
MAA

Résumez ce que nous pouvons faire et ne pouvons pas:

Préparation:

private Set<String> words = new HashSet<>(Arrays.asList("existing Word"));

Final par référence

private final Set<String> words = new HashSet<>();

peut

words.add("new Word");

ne peut pas

words = new HashSet<>(); //compilation error

Final par référence et non modifiable par collection.

private final Set words = Collections.unmodifiableSet (mots);

peut

String Word = words.iterator().next();

ne peut pas

words = new HashSet<>(); // compilation error
words.add("new Word"); // runtime error UnsupportedOperationException

Final par référence et non modifiable par collection mais mutuel comme objet de collection.

Mais si vous avez la collection avec mutual objects, vous pouvez CHANGER l'état interne de cet objet.

 class A {
       public int a; //mutable field. I can change it after initialization
       public A(int a) {this.a = a;}
     }

 private final Set<A> set = Collections.unmodifiableSet(Arrays.asList(new A(25)));

Je ne peux toujours pas

set = new HashSet<>(); // compilation error
set.add(new A(777)); // runtime error UnsupportedOperationException

Mais can

 A custom = words.iterator().next(); //here custom.a = 25;
 custom.a = 777; //here first element of **final, unmodifible** collection 
    //was changed from 25 to 777
0
Dmitry N.