在我的应用程序上工作时,我遇到了一个我没想过或以前遇到过的行为。
考虑这个简单的类:
public class A {
public long id;
public long date;
public List<Long> list;
/* constructors */
}
现在考虑这两种方法来做同样的事情:
/* 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不能按预期工作。
代码实际上是在一个循环中运行的,并且预计在A
内可能存在mList
类型的各种对象,以及long
的未知(超过1)量每个对象的list
字段内的值。
真正发生的是第一种方法工作正常,而第二种方法导致每个对象long
内总有1 list
值的情况(即使应该有更多)
我个人看不出有什么可能导致它以这种方式工作,这就是为什么我在这里,要求找到这个'神秘'的答案。我最疯狂的猜测会说它与指针有关,或者可能是我不知道的List<T>
的一些默认行为。
话虽如此,这可能是导致这种意外行为的原因吗?
P.S:我确实尝试在发布之前进行搜索,但我真的不知道要搜索什么,所以我找不到任何有用的东西。答案 0 :(得分:1)
这是因为mList.contains(mA)
通过调用o1.equals(o2)
在内部检查对象是否相等。 equals()
的默认实现如下所示:
public boolean equals(Object o) {
return this == o;
}
显然实例并不相同,所以每次都要添加一个新实例。覆盖班级equals()
中的A
以解决问题。我猜如果它们具有相同的id,实例是相同的吗?
public boolean equals(Object o) {
return this.mId == o.mId;
}
答案 1 :(得分:1)
第二种方法导致每个对象内部列表中总有1个长值的情况(即使应该有更多)。
<强>问题强>
A mA = new A(id, date);
if(!mA.list.contains(mLong))
正如您所看到的,您没有从mList获取A类的引用,并且您正在检查刚刚创建的列表中是否包含long值,它只会添加一个。所以基本上你正在做的是创建一个新的类A,在long列表中有一个long值并添加到mList
另一方面,你的第一个方法是获取已经添加的类A的实例,并检查列表中是否包含该长度,如果没有,则将其添加到列表中。
答案 2 :(得分:0)
如果有方法mList.addIfAbsent(mA)
(在将其添加到列表后返回mA
,或者已经存在且在mA
上匹配equals
的对象,则返回mA = mList.addIfAbsent(mA);
mA.list.addIfAbsent(mLong);
,它会使你的操作像
mA
在你的第二个例子中,当addIfAbsent(mA)
等价物已经在那里时,你显然会破坏这种机制。基本上,如果列表中没有其他对象与它相等,则将mA
的定义更改为&#34;将mA
添加到列表中,然后返回int indOfOld = mList.indexOf(ma);
if (indOfOld != -1)
ma = mList.get(indOfOld);
else
mList.add(mA);
if(!mA.list.contains(mLong))
mA.list.add(mLong);
。&#34;
你可以提高性能,实现与你的第二个例子(没有错误)相同的结果:
HashSet
这不会削减你的大O复杂性,但至少会只做一次O(n)操作(与你工作代码中的两个相比)。
对你和其他人来说,这可能是显而易见的;如果是这样,请原谅 - 但如果这些列表中的元素数量超过数千个,那么如果您使用LinkedHashSet
,则可以获得显着的性能提升,如果您关心插入,则可以获得add
订购。在这种情况下,您只需尝试false
一个对象,如果它已经存在,则获取get(mA)
,这将花费您O(1)时间。然后你可以indexOf
来自集合,而不是使用{{1}}的迂回方式,也可以在O(1)时间内。