web-dev-qa-db-ja.com

Collections.unmodifiableSet()はJavaで何をしますか?

わかります - Collections.unmodifiableSet は、指定されたセットの変更不可能なビューを返しますが、final修飾子を使用してこれを実行できない理由がわかりません。

私の理解では、finalは定数を宣言します。変更できないものです。したがって、セットが定数として宣言されている場合、そのセットを変更することはできません。セットから削除したり、追加したりすることはできません。

なぜCollections.unmodifiableSet

33
Roman

finalは、変更できないオブジェクト参照を宣言します。

_private final Foo something = new Foo();
_

新しいFooを作成し、参照をsomethingに配置します。その後、somethingを変更してFooの別のインスタンスを指すようにすることはできません。

notはオブジェクトの内部状態の変更を防ぎます。関連するスコープにアクセスできるFooのメソッドを呼び出すことができます。これらのメソッドの1つ以上がそのオブジェクトの内部状態を変更する場合、finalはそれを防止しません。

そのため、次のとおりです。

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

しないnot追加または変更できないSetを作成します。これは、fixedがそのインスタンスのみを参照することを意味します。

対照的に、次のようにします。

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

たとえば、fixed.add()またはfixed.remove()を呼び出そうとした場合にSetをスローするUnsupportedOperationExceptionのインスタンスを作成します。たとえば、オブジェクト自体が内部状態を保護し、変更されないようにします。

完全を期すために:

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

内部状態を変更できないSetのインスタンスを作成します。また、fixedがそのセットのインスタンスのみを指すことも意味します。

finalを使用してプリミティブの定数を作成できる理由は、値を変更できないという事実に基づいています。上記のfixedは単なる参照であり、変更できないアドレスを含む変数であることを思い出してください。まあ、プリミティブ、例えば.

_private final int ANSWER = 42;
_

ANSWERの値は42です。ANSWERは変更できないため、値が42になるだけです。

すべての線をぼかす例は次のようになります:

_private final String QUESTION = "The ultimate question";
_

上記のルールに従って、QUESTIONには、「究極の質問」を表すStringのインスタンスのアドレスが含まれ、そのアドレスは変更できません。ここで覚えておくべきことは、String自体は不変であることです。これを変更するStringのインスタンスに対しては何も実行できません。また、そうしないと実行される操作(replacesubstringなど)は、まったく異なる参照を返します。 Stringのインスタンス。

65
Rob

finalは、変数が表すオブジェクトへのreferenceが変更できないことのみを保証します。オブジェクトのインスタンスとその可変性に対しては何もしません。

final Set s = new Set();は、もう一度s = new Set();を実行できないことを保証するだけです。それはセットを変更不可能にしません、もしあなたがそれに最初に何かを追加することができなかった場合。つまり、明確にするために、finalは変数referenceにのみ影響し、参照が指すオブジェクトには影響しません。

私は次のことができます:

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

しかし、私はこれを行うことができません。

_l = new ArrayList<String>();
_

ここでも、finalのため、変数lが指すものを変更できません。

コレクションコンテナのスレッドセーフを実現するには、次の3つのいずれかを行う必要があります。

_Java.util.Collections.syncronizedXXX();
_

または

_Java.util.Collections.unmodifiableXXX();
_

または、_Java.util.concurrency.* package_の適切なコンテナのいずれかを使用します。

Personオブジェクトがあり、final Person p = new Person("me");を実行した場合、pを再割り当てして別のPersonオブジェクトを指すことができないことを意味します。まだできますp.setFirstName("you");

状況を混乱させるのは

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

c ++ではconstのように見えますが、実際には、それらが指すオブジェクトは本質的に不変/変更不可能です。オブジェクトの内部状態を変更できるミューテーターメソッドを含むコンテナーまたはオブジェクトはconstではありません。これらのオブジェクトへのreferencefinalであり、再割り当てできません参照別のオブジェクト。

15
user177800

Collections.unmodifiableSet(Set<? extends T>)は、元のセットにラッパーを作成します。このラッパーセットは変更できません。ただし、元のセットは変更できます。

例:

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

いくつかの要素を追加する

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

追加された要素の印刷

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

actualSetを変更不可能なセットに入れ、新しい参照(wrapperSet)に割り当てます。

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

WrapperSetを印刷します。そのため、actualSetの値になります

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

wrapperSetの1つの要素を削除/追加してみましょう。

_wrapperSet.remove("aaa");   //UnSupportedOperationException 
_

actualSetに要素をもう1つ追加します

_    actualSet .add("ccc");
_

actualSetwrapperSetを印刷します。両方のセットの値は同じです。そのため、実際のセットで要素を追加/削除すると、変更はラッパーセットにも反映されます。

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

使用法:

このCollections.unmodifiableSet(Set<? extends T>)は、オブジェクトのSetのゲッターメソッドが変更されないようにするために使用されます。言わせて

_public class Department{

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

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

finalは(C++スタイル)constではありません。 C++とは異なり、Javaにはconst- methodsなどはなく、オブジェクトを変更できるメソッドはfinal参照を介して呼び出すことができます。

Collections.unmodifiable*は、関係するコレクションの読み取り専用を(コンパイル時ではなく実行時にのみ)強制するラッパーです。

3

できることとできないことを要約します。

準備:

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

参照による最終

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

can

words.add("new Word");

できない

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

参照によって最終的に、コレクションによって変更不可。

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

can

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

できない

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

参照によって最終的になり、コレクションによって変更できませんが、コレクションのオブジェクトとして相互です。

しかしmutual オブジェクトを含むコレクションがある場合、そのオブジェクトの内部状態を変更できます。

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

まだできません

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

でもできます

 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
Novdar