懒惰地重新初始化一个使用Collections.emptyList()声明的列表为可修改列表。

huangapple go评论79阅读模式
英文:

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

请注意,最后一个方法中的更改不会影响传递给方法的列表参数,因为Java是按值传递的。所以,最后一个方法可能不会按预期工作。

英文:

Want to declare a list as List<String> info = Collections.emptyList()
but when user calls add(String msg) then re-init to a modifiable list.

Is this the correct way:

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

Or

if(info.equals(e)){

If I have 3 of these can I have this common code :

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

I guess the following wont work due to the new list being created?

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

答案1

得分: 2

请注意,即使Collections.emptyList()始终返回保存在Collections.EMPTY_LIST中的一个实例,引用比较不能检测到调用者是否使用了JDK 9+的List.of()来初始化字段。另一方面,非空也不一定保证可变性。

整个逻辑仅适用于所有调用者及其使用情况已知的private方法。

但您应该考虑彻底放弃这些特殊情况的替代方案。自从Java 8以来,默认构造函数new ArrayList<>()将不会创建后备数组。它将推迟到首次添加元素时才执行。

因此,您可以使用简单的new ArrayList<>()初始化所有字段,并使用简单的add调用实现addInfoaddWarnaddErr,摆脱addTo方法、条件语句和重复赋值。甚至可以将字段声明为final。即使对于未使用的列表,仍然不需要大量内存。

英文:

Note that even if Collections.emptyList() always returns the one instance held in Collections.EMPTY_LIST, a reference comparison does not detect when a caller used JDK 9+ List.of() to initialize the field. On the other hand, being non-empty does not guaranty mutability either.

The entire logic is suitable only for a private method were all callers and their usage are known.

But you should consider the alternative of dropping these special cases altogether. Since Java 8, the default constructor new ArrayList<>() will not create a backing array. It is deferred until the first addition of an element.

So you can initialize all fields with a plain new ArrayList<>() and implement the addInfo, addWarn, and addErr with a plain add call, getting rid of the addTo method, the conditionals, and the repeated assignments. Even declaring the fields final is possible. While still not requiring a significant amount of memory for the unused lists.

答案2

得分: 1

考虑您的代码:

final List<String> e = Collections.emptyList();
if(info == e){
    info = new ArrayList<>();
}
info.add(s);

我不认为在Java API中有任何保证emptyList()始终会返回相同的引用(javadoc中指出:“此方法的实现不需要为每次调用创建单独的List对象”)。

考虑到您可能会修改列表,使用new ArrayList<>()进行初始化比使用emptyList()更有意义。实际上,使用可能需要进行修改的不可修改列表真的没有太多意义。

然而,如果您真的出于某种原因需要使用emptyList(),那么也许可以这样做:

if (info.isEmpty())
    info = new ArrayList<>();

考虑到您即将向其中添加一个项目,此测试只会通过一次。

英文:

Considering your code:

    final List&lt;String&gt; e = Collections.emptyList();
if(info == e){
info = new ArrayList&lt;&gt;();
}
info.add(s);

I don't believe there's any guarantee in the Java API that the same reference will always be returned from emptyList() (the javadoc states "Implementations of this method need not create a separate List object for each call").

Given you may modify the list it'd make more sense to initialise with new ArrayList&lt;&gt;() rather than emptyList(). Really doesn't make much sense to use an unmodifiable list that you may want to modify.

However if you really need to use emptyList() for some reason, then perhaps:

if (info.isEmpty())
info = new ArrayList&lt;&gt;();

Given you are about to add an item to it this test will only pass once anyway.

答案3

得分: 1

使用.equals是唯一正确的解决方案,但与更简单的info.isEmpty()等效。

英文:

Using .equals is the only correct solution -- but equivalent to the much simpler info.isEmpty().

huangapple
  • 本文由 发表于 2020年10月7日 06:49:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64234824.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定