jAVA中列表<T>的安卓意外行为
在使用我的应用程序时,我遇到了一个我没有预料到或以前遇到过的行为
考虑这个简单类:
public class A {
public long id;
public long date;
public List<Long> list;
/* constructors */
}
现在考虑这2种方法来做同样的事情:
/* Approach #1 */
List<A> mList = new ArrayList<A>();
long mLong = ......;
A mA = new A(id, date);
if(!mList.contains(mA))
mList.add(mA);
mA = mList.get(mList.indexOf(mA));
if(!mA.list.contains(mLong))
mA.list.add(mLong);
/* Approach #2 */
List<A> mList = new ArrayList<A>();
long mLong = ......;
A mA = new A(id, date);
if(!mA.list.contains(mLong))
mA.list.add(mLong);
if(!mList.contains(mA))
mList.add(mA);
如您所见,方法2比方法1更有效,也更容易理解
然而,显然,方法2并没有如预期的那样起作用
代码实际上是在循环中运行的,在mList
内可能有各种类型的A
对象,在每个对象的list
字段内可能有未知(超过1)数量的long
值
实际情况是,第一种方法工作正常,而第二种方法的结果是,每个对象的list
内总是有1long
个值(即使应该有更多)
我个人看不出是什么可能导致它以这种方式工作,这就是为什么我在这里,寻求这个“谜”的答案。我最疯狂的猜测是它与指针有关,或者可能与我不知道的List<T>
的某些默认行为有关
话虽如此,这种意外行为的原因可能是什么强>
附言:我在发帖前确实尝试过搜索,但我真的不知道要搜索什么,所以我没有找到任何有用的东西
# 1 楼答案
第二种方法的结果是,在每个对象的列表中总是有一个长值(即使应该有更多)
问题
A mA = new A(id, date); if(!mA.list.contains(mLong))
正如您所见,您没有从mList获取类A的引用,您正在检查刚刚创建的列表中是否包含long值,该列表只会添加一个。所以基本上你要做的是创建一个新的a类,在long列表中有1个long值,并添加到mList中
另一方面,您的第一种方法是获取已经添加的类A的实例,并检查该long是否包含在列表中,如果没有,则将其添加到long列表中
# 2 楼答案
这是因为
mList.contains(mA)
通过调用o1.equals(o2)
在内部检查对象的相等性。equals()
的默认实现如下所示:显然,这些实例并不相同,所以每次都要添加一个新实例。重写类
equals()
中的A
以修复该问题。如果它们有相同的id,我猜它们是相同的# 3 楼答案
如果有一个方法
mList.addIfAbsent(mA)
(在将其添加到列表后返回mA
,或者返回已经存在并与equals
上的mA
匹配的对象),它将使您的操作变得非常简单在第二个例子中,当
mA
等价物已经存在时,显然打破了这种机制。基本上,您可以将addIfAbsent(mA)
的定义更改为“如果列表中没有其他对象与之相等,则将mA
添加到列表中,并返回mA
”您可以提高性能,获得与第二个示例(无缺陷)相同的结果,如下所示:
这不会降低big-O的复杂度,但至少只需要一个O(n)操作(相比之下,工作代码中只有两个)
顺便说一句,这对你和其他人来说可能是显而易见的;如果是这样的话,请原谅,但是如果这些列表大于数千个元素,如果您使用
HashSet
,或者如果您关心插入顺序,甚至可以使用LinkedHashSet
,那么性能可能会显著提高。在这种情况下,您只需尝试add
一个对象,如果它已经存在,则获取false
,这只需要花费O(1)个时间。然后你将get(mA)
从集合中,而不是你与indexOf
的迂回方式,同样在O(1)时间内