最终的ArrayList是什么意思?


68

通过使ArrayList(或其他Collection)成为最终形式,我们可以获得哪些优点/缺点?我仍然可以将新元素添加到ArrayList中,删除元素并进行更新。但是,什么才是最终的效果呢?


1
当将数组声明为final时,可以修改存储在数组中的对象的状态。您需要使其不可变,以免进行修改。
Pankaj Kumar 2012年

Answers:


134

但是,什么才是最终的效果呢?

这意味着您不能重新绑定变量以指向其他集合实例

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

4
+1使用final字段可以提高清晰度,因为类可能很长。我没有使用final in方法,而是尝试分解长方法。
彼得·劳瑞

如何final提高清晰度?
约翰D

15

这仅意味着您无法重新分配其参考。尝试执行以下操作将导致编译器错误。

final List<String> list = new ArrayList<String>();

list = new LinkedList<String>();
     ^
     Compiler error here

如果您确实想要一个不可变的列表,则应使用该Collections.unmodifiableList()方法。



5

使变量 final可确保您在分配该目标引用后不能重新分配该目标。如您所述,您仍然可以使用列出的方法进行更改。

如果您将final关键字与Collections.unmodifiableList结合使用,则会产生可能要实现的行为,例如:

final List fixedList = Collections.unmodifiableList(someList);

结果导致fixedList不能更改由指向的列表。但是请注意,仍然可以通过someList引用对其进行更改(因此,请确保在此赋值后它不在范围内。)


5

final 在多线程下有很多后果。

  1. JMM明确定义了final保证字段初始化的完成。

没有明确定义的是:

  1. 编译器可以随意跨内存障碍对其进行重新排序。
  2. 编译器始终可以读取缓存的副本。

4

正确观察它不会影响您对ArrayList所做的操作-ArrayList本身仍然可变。您刚刚使引用不可变。

但是将变量定为final确实还有其他好处:

  • 如果期望变量保持不变,则可以防止更改该变量。这可以帮助防止将来的错误。
  • 制作变量final可以帮助编译器进行某些性能优化。

通常,使不变的事物越多越好。因此,使引用成为最终引用(即使它们是对可变对象的引用)通常是一个好主意。


1

Final是Java中的关键字或保留字,可以应用于Java中的成员变量,方法,类和局部变量。一旦将引用定为最终引用,就不允许更改该引用,并且如果尝试在Java中重新初始化最终变量,则编译器将对此进行验证并引发编译错误。


1

您不能像aix所说的那样将其重新绑定到其他集合。

示例:结合不可变列表实现,您可以获得可以公开使用的安全成员。

示例:当您依赖于该引用不变时,您需要最终的。例如,在同步方案中就是如此。

可能还有更多的例子。如果您根本不打算更改引用,则将成员声明为final是一种好方法。


1

要获得一个真正不变的列表,您必须对列表内容进行深层复制。UnmodifiableList只会使引用列表有些不可变。现在,随着大小的增加,对列表或数组进行深拷贝将对内存造成困难。您可以使用序列化/反序列化并将数组/列表的深层副本存储到临时文件中。该设置程序将不可用,因为成员变量需要是不变的。getter会将成员变量序列化为文件,然后对其进行反序列化以获得深层副本。序列化具有进入对象树深处的天生本质。不过,这将确保完全不变,但会降低性能。

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

1

我个人将班级的collections字段标记为final,以免班级的用户检查是否为null。之所以可行,是因为一旦将值分配给了最终变量,就永远无法将其重新分配给另一个值,包括null。


0

从本质上讲,您想达到的目的是使列表不可变。但是,当您将列表引用标记为final时,则意味着该引用不能指向除此以外的任何其他列表对象。

如果要使用最终的(不可变的)ArrayList,请改用Collections类的实用程序方法Collections.unmodifaibleList(list)。


0

我想到了这个问题,并从另一个角度对示例进行了编码和添加。

最终的arrayList仍然可以修改,请参考下面的示例并运行它以供参考。

这是带有不可变List声明的不可变类:

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();。在这里的主要方法。

修改部分确实帮助我理解了这一点,并希望以同样的方式有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.