在使用 Collections.emptyList() 声明后,延迟将列表重新初始化为可修改
Lazy reinit a list to modifiable after declaring with Collections.emptyList()
想要将列表声明为 List<String> info = Collections.emptyList()
但是当用户调用 add(String msg) 然后重新初始化到一个可修改的列表时。
这样正确吗:
private List<String> info = Collections.emptyList();
public void addInfo(String s){
final List<String> e = Collections.emptyList();
if(info == e){
info = new ArrayList<>();
}
info.add(s);
}
或
if(info.equals(e)){
如果我有其中的 3 个,我可以得到这个通用代码:
public void addInfo(String s) {
info = addTo(s, info);
}
public void addWarn(String s) {
warn = addTo(s, warn);
}
public void addErr(String s) {
errs = addTo(s, errs);
}
private List<String> addTo(String s, @org.jetbrains.annotations.NotNull List<String> t){
final List<String> e = Collections.emptyList();
if(t.equals(e)){
t = new ArrayList<>();
}
t.add(s);
return t;
}
我想由于正在创建新列表,以下内容将无法正常工作?
private void addTo(String s, @org.jetbrains.annotations.NotNull List<String> t){
final List<String> e = Collections.emptyList();
if(t.equals(e)){
t = new ArrayList<>();
}
t.add(s);
}
考虑您的代码:
final List<String> e = Collections.emptyList();
if(info == e){
info = new ArrayList<>();
}
info.add(s);
我不相信 Java API 中有任何保证相同的引用将始终从 emptyList()
返回(javadoc 声明“此方法的实现不需要为每个调用创建一个单独的列表对象")。
鉴于您可能会修改列表,因此使用 new ArrayList<>()
初始化比 emptyList()
更有意义。使用您可能想要修改的不可修改列表确实没有多大意义。
但是,如果您出于某种原因确实需要使用 emptyList()
,那么也许:
if (info.isEmpty())
info = new ArrayList<>();
鉴于您即将向其中添加一个项目,此测试无论如何只会通过一次。
使用 .equals
是唯一正确的解决方案 -- 但等同于更简单的 info.isEmpty()
.
请注意,即使 Collections.emptyList()
总是 returns 在 Collections.EMPTY_LIST
中保存的一个实例,参考比较也不会检测调用者何时使用 JDK 9+ List.of()
初始化字段。另一方面,非空也不能保证可变性。
整个逻辑仅适用于 private
方法,所有调用者及其用法都是已知的。
但是您应该考虑完全放弃这些特殊情况的替代方案。自 Java 8 起,默认构造函数 new ArrayList<>()
将不会创建后备数组。它被推迟到第一次添加元素时。
所以你可以用普通的 new ArrayList<>()
初始化所有字段,并用普通的 add
调用实现 addInfo
、addWarn
和 addErr
,摆脱 addTo
方法、条件和重复赋值。甚至声明字段 final
也是可能的。虽然仍然不需要大量内存来存储未使用的列表。
想要将列表声明为 List<String> info = Collections.emptyList()
但是当用户调用 add(String msg) 然后重新初始化到一个可修改的列表时。
这样正确吗:
private List<String> info = Collections.emptyList();
public void addInfo(String s){
final List<String> e = Collections.emptyList();
if(info == e){
info = new ArrayList<>();
}
info.add(s);
}
或
if(info.equals(e)){
如果我有其中的 3 个,我可以得到这个通用代码:
public void addInfo(String s) {
info = addTo(s, info);
}
public void addWarn(String s) {
warn = addTo(s, warn);
}
public void addErr(String s) {
errs = addTo(s, errs);
}
private List<String> addTo(String s, @org.jetbrains.annotations.NotNull List<String> t){
final List<String> e = Collections.emptyList();
if(t.equals(e)){
t = new ArrayList<>();
}
t.add(s);
return t;
}
我想由于正在创建新列表,以下内容将无法正常工作?
private void addTo(String s, @org.jetbrains.annotations.NotNull List<String> t){
final List<String> e = Collections.emptyList();
if(t.equals(e)){
t = new ArrayList<>();
}
t.add(s);
}
考虑您的代码:
final List<String> e = Collections.emptyList();
if(info == e){
info = new ArrayList<>();
}
info.add(s);
我不相信 Java API 中有任何保证相同的引用将始终从 emptyList()
返回(javadoc 声明“此方法的实现不需要为每个调用创建一个单独的列表对象")。
鉴于您可能会修改列表,因此使用 new ArrayList<>()
初始化比 emptyList()
更有意义。使用您可能想要修改的不可修改列表确实没有多大意义。
但是,如果您出于某种原因确实需要使用 emptyList()
,那么也许:
if (info.isEmpty())
info = new ArrayList<>();
鉴于您即将向其中添加一个项目,此测试无论如何只会通过一次。
使用 .equals
是唯一正确的解决方案 -- 但等同于更简单的 info.isEmpty()
.
请注意,即使 Collections.emptyList()
总是 returns 在 Collections.EMPTY_LIST
中保存的一个实例,参考比较也不会检测调用者何时使用 JDK 9+ List.of()
初始化字段。另一方面,非空也不能保证可变性。
整个逻辑仅适用于 private
方法,所有调用者及其用法都是已知的。
但是您应该考虑完全放弃这些特殊情况的替代方案。自 Java 8 起,默认构造函数 new ArrayList<>()
将不会创建后备数组。它被推迟到第一次添加元素时。
所以你可以用普通的 new ArrayList<>()
初始化所有字段,并用普通的 add
调用实现 addInfo
、addWarn
和 addErr
,摆脱 addTo
方法、条件和重复赋值。甚至声明字段 final
也是可能的。虽然仍然不需要大量内存来存储未使用的列表。