为啥 List.add(E) 返回 boolean 而 List.Add(int, E) 返回 void?

Posted

技术标签:

【中文标题】为啥 List.add(E) 返回 boolean 而 List.Add(int, E) 返回 void?【英文标题】:Why does List.add(E) return boolean while List.Add(int, E) returns void?为什么 List.add(E) 返回 boolean 而 List.Add(int, E) 返回 void? 【发布时间】:2014-08-02 02:41:45 【问题描述】:

看着javadoc,我看到一个ArrayList 有一个重载的add 方法:

公共布尔加法(E e)

将指定元素附加到此列表的末尾。

public void add(int index, E元素)

在此列表中的指定位置插入指定元素。将当前位于该位置的元素(如果有)和任何后续元素向右移动(将它们的索引加一)。

我注意到第一个返回了boolean,而第二个返回了void。事实证明,第一个 add 必须返回 boolean,因为:

返回: true(由 Collection.add(E) 指定)

于是我去了Collection.add(E):

布尔加法(E e)

确保此集合包含指定的元素(可选操作)。如果此集合因调用而更改,则返回 true。 (如果此集合不允许重复且已包含指定元素,则返回 false。)

所以我的问题是,为什么 add 指定返回布尔值而不是 void?当我add 某事我希望只做一个操作。

我了解,与 ArrayList 不同,还有其他数据结构不允许重复(例如集合)。但即便如此,问题还是不能通过以下方式解决:

public void add(E e)
    if(e is not in set)
        add e;
    

这样,如果e 在集合中,则不会采取任何行动。为什么返回 boolean 而不是 void 方法更好?

【问题讨论】:

我的猜测是因为有时您可能想知道您是否未能将某些东西放入集合中。如果返回 void ,则无法知道除了查找之后的元素。虽然我想不出任何相关的例子...... 您的代码不会做同样的工作,因为它不会让调用者知道它是否已经包含该对象。等效代码在 .add 之前会有一个 .contains 如果您不关心对add 的调用是否导致任何更改,您没有义务对结果做任何事情。但其他人可能会发现这些信息很有用。 一个使用示例是我最近使用的一个:一个处理队列和一个搜索算法,它只在邻居是新邻居时查看图块并将邻居添加到队列中(否则,如果你弯曲,你将永远搜索回到自己身上)。显然可以通过几种不同的方式实现,但这是一个 另一个需要考虑的好处是同步集合的原子性。 【参考方案1】:

Collection.add 是一种非常通用的方法(在 Java 泛型的意义上不是——在广泛适用的意义上)。因此,他们想要一个普遍适用的返回值。

一些类(如ArrayList)总是接受元素,所以总是返回true。没错,在这些情况下,void 的返回类型也一样好。

但其他人,例如Set,有时不允许添加元素。在Set 的情况下,如果一个相等的元素已经存在,就会发生这种情况。知道这一点通常很有帮助。另一个例子是有界集合(只能容纳一定数量的元素)。

您可能会问,“代码不能手动检查吗?”例如,有一个集合:

if (!set.contains(item)) 
    set.add(item);
    itemWasAdded(item);

这比你现在可以做的更详细,但不是很多:

if (set.add(item)) 
    itemWasAdded(item);

但这种检查然后执行的行为不是线程安全的,这在多线程应用程序中可能至关重要。例如,可能是另一个线程在您检查第一个代码 sn-p 中的 set.contains(item)set.add(item) 之间添加了一个相等的项目。在多线程场景中,这两个动作确实需要是一个单一的原子动作;从方法中返回boolean 使这成为可能。

【讨论】:

not in the sense of Java generics - 感谢上帝! :) +1,很好的答案。 检查然后添加对于某些类型的实现也可能效率低下,因此 API 允许您在这些情况下以更好的方式执行此操作。 感谢您的回答。我不同意一个细节。 '返回类型的 void 也一样好'。到目前为止,我还没有发现任何返回 void 是一个好主意的情况。我宁愿返回添加的对象而不是 void。【参考方案2】:

因为它是一个额外的信息,不需要任何成本,并且在某些情况下很有用。例如:

Set<String> set = new HashSet<String>();
set.add("foobar");
boolean wasAlreadyThere = set.add("foobar");

否则你将不得不这样做

boolean wasAlreadyThere = set.contains("foobar");
set.add("foobar");

这需要两倍的工作(首先您必须查找,然后再次查找以添加)。

【讨论】:

不要让所有的书呆子,但是...不是第一个例子中的逻辑倒退吗?【参考方案3】:

因为了解某些内容是否实际添加或已经存在通常很有用。

【讨论】:

这个答案更适合作为评论。 所有答案都适合作为评论,答案实际上在 javadocs -> Returns: true if this collection changed as a result of the call 中说明了一切。 ...不公平的投票顺便说一句... @A4L 这并不公平。对此的真正原因没有任何解释。 我同意你的观点,但它肯定回答了 OP 的一半问题并且没有错,因此可能不值得投赞成票,但肯定不值得投反对票。接受的答案与此内容完全相同,因为它没有回答有关List#add(int, E) 的部分,只是更详细一点,但是投票赞成,这很奇怪 我投反对票的原因是,为什么这两个调用有不同的返回类型。如果有人问“为什么许多澳大利亚哺乳动物都有育儿袋,而这在世界其他地方很少见?”,我会否决答案:“这样它们就可以携带婴儿”。

以上是关于为啥 List.add(E) 返回 boolean 而 List.Add(int, E) 返回 void?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中的List的使用

List.Add 正在替换索引 0 而不是添加到下一个位置。为啥?

LeetcodeSort Array By Parity II

`PriorityQueue` 中的 `add` 和 `addAll` 行为不同,这是为啥呢?

java里set list 为啥能遍历集合

sql查询语句中所带参数,在windows环境和linux环境java工程中为啥表现不同?