ArrayList(または他のコレクション)を最終的にすることで得られる長所/短所はどれですか?それでも、ArrayListに新しい要素を追加したり、要素を削除したり、更新したりできます。しかし、それを最終的なものにする効果は何ですか?
13 に答える
しかし、それを最終的なものにする効果は何ですか?
これは、別のコレクションインスタンスを指すように変数を再バインドできないことを意味します。
final List<Integer> list = new ArrayList<Integer>();
list = new ArrayList<Integer>(); // Since `list' is final, this won't compile
スタイルの問題として、私は変更するつもりのないほとんどの参照をとして宣言しfinal
ます。
それでも、ArrayListに新しい要素を追加したり、要素を削除したり、更新したりできます。
必要に応じて、以下を使用して挿入、削除などを防ぐことができますCollections.unmodifiableList()
。
final List<Integer> list = Collections.unmodifiableList(new ArrayList<Integer>(...));
これは、参照を再割り当てできないことを意味します。以下のようなことを行おうとすると、コンパイラエラーが発生します。
final List<String> list = new ArrayList<String>();
list = new LinkedList<String>();
^
Compiler error here
本当に不変のリストが必要な場合は、このCollections.unmodifiableList()
メソッドを使用する必要があります。
new ArrayList
たとえば、その参照を変更することはできません。
変数final
を作成すると、割り当て後にそのオブジェクト参照を再割り当てできないようになります。あなたが言及するように、あなたはまだ変更を加えるためにメソッドをリストすることを使うことができます。
final
キーワードをCollections.unmodizableListの使用と組み合わせると、おそらく達成しようとしている動作が得られます。たとえば、次のようになります。
final List fixedList = Collections.unmodifiableList(someList);
その結果、が指すリストはfixedList
変更できません。ただし、参照によって変更される可能性があることに注意してsomeList
ください(したがって、この割り当ての後で範囲外になっていることを確認してください)。
final
マルチスレッドでは多くの影響があります。
- JMMは、
final
フィールドの初期化の完了が保証されることを明確に定義しています。
明確に定義されていないものは次のとおりです。
- コンパイラは、メモリバリアを越えて自由に並べ替えることができます。
- コンパイラは常にキャッシュされたコピーを読み取ることができます。
正しく観察しているように、ArrayListで実行できることには影響しません。ArrayList自体は引き続き変更可能です。参照を不変にしました。
しかし、変数finalを作成することには、他の利点があります。
- 一定であることが期待される場合、変数が変更されるのを防ぎます。これは、将来のバグを防ぐのに役立ちます。
- 変数を作成
final
すると、コンパイラーが特定のパフォーマンスを最適化するのに役立ちます。
一般に、不変にするものが多いほど、優れています。したがって、参照を最終的にすることは(たとえそれらが可変オブジェクトへの参照であっても)一般的には良い考えです。
Finalは、Javaのキーワードまたは予約語であり、Javaのメンバー変数、メソッド、クラス、およびローカル変数に適用できます。参照をfinalにすると、その参照を変更することはできなくなります。Javaでfinal変数を再初期化しようとすると、コンパイラはこれを検証し、コンパイルエラーを発生させます。
aixが言ったように、それを別のコレクションに再バインドすることはできません。
例:不変リストの実装と組み合わせて、公開できる安全なメンバーを取得します。
例:その参照が変更されないことに依存している場合は、finalが必要です。これは、たとえば同期シナリオに当てはまります。
もっと多くの例があるかもしれません。参照をまったく変更する予定がない場合は、メンバーを最終として宣言することをお勧めします。
クラスのコレクションフィールドに個人的にマークを付けて、クラスfinal
のユーザーがnullかどうかをチェックしないようにします。これが機能するのは、値がすでに最終変数に割り当てられると、nullを含む別の値に再割り当てすることができないためです。
本当に不変のリストを取得するには、リストの内容の深いコピーを作成する必要があります。UnmodizableListは、参照のリストをある程度不変にするだけです。リストまたは配列のディープコピーを作成することは、サイズが大きくなるにつれてメモリ上で困難になります。シリアル化/逆シリアル化を利用して、配列/リストのディープコピーを一時ファイルに保存できます。メンバー変数は不変である必要があるため、セッターは使用できません。ゲッターは、メンバー変数をファイルにシリアル化し、それを非シアル化してディープコピーを取得します。Seraializationには、オブジェクトツリーの深部に入るという生来の性質があります。ただし、これにより、ある程度のパフォーマンスコストで完全な不変性が保証されます。
package com.home.immutable.serial;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public final class ImmutableBySerial {
private final int num;
private final String str;
private final ArrayList<TestObjSerial> immutableList;
ImmutableBySerial(int num, String str, ArrayList<TestObjSerial> list){
this.num = num;
this.str = str;
this.immutableList = getDeepCloned(list);
}
public int getNum(){
return num;
}
public String getStr(){
return str;
}
public ArrayList<TestObjSerial> getImmutableList(){
return getDeepCloned(immutableList);
}
private ArrayList<TestObjSerial> getDeepCloned(ArrayList<TestObjSerial> list){
FileOutputStream fos = null;
ObjectOutputStream oos = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
ArrayList<TestObjSerial> clonedObj = null;
try {
fos = new FileOutputStream(new File("temp"));
oos = new ObjectOutputStream(fos);
oos.writeObject(list);
fis = new FileInputStream(new File("temp"));
ois = new ObjectInputStream(fis);
clonedObj = (ArrayList<TestObjSerial>)ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return clonedObj;
}
}
私はこれと同じ質問を考え、コード化して例を示し、さらに別の角度から説明を追加するようになりました。
最終的なarrayListは引き続き変更できます。以下の例を参照して実行し、自分で確認してください。
不変のリスト宣言を持つ不変のクラスは次のとおりです。
public final class ImmutableClassWithArrayList {
final List<String> theFinalListVar = new ArrayList<String>();
}
そしてここにドライバーがあります:
public class ImmutableClassWithArrayListTester {
public static void main(String[] args) {
ImmutableClassWithArrayList immClass = new ImmutableClassWithArrayList();
immClass.theFinalListVar.add("name");
immClass.theFinalListVar.forEach(str -> System.out.println(str));
}
}
ご覧のとおり、主な方法はリストを追加(変更)することです。したがって、注意すべき唯一のことは、コレクションタイプのオブジェクトへの「参照」を別のそのようなオブジェクトに再割り当てできないことです。上記のadarshrによる回答のように、immClass.theFinalListVar = new ArrayList();を実行することはできません。ここのメインメソッドで。
変更部分は私がこれを理解するのに本当に役立ち、同じように役立つことを願っています。
基本的に、ここで達成しようとしているのは、リストを不変にすることだと思います。ただし、リスト参照をfinalとマークすると、参照がこれ以外の他のリストオブジェクトを指すことができないことを意味します。
最終的な(不変の)ArrayListが必要な場合は、代わりに、CollectionsクラスのユーティリティメソッドCollections.unmodifaibleList(list)に移動します。
ArrayListをfinalとして宣言した場合でも、要素を追加することはできますが、再割り当てすることはできません。
final ArrayList<String> list = new ArrayList<>();
list.add("abc"); //allowed
list.add("pqr"); //allowed
list = new ArrayList<>(); // Not allowed