在jAVA中使用List <t>的意外行为</t>

时间:2014-08-08 20:04:19

标签: java android list

在我的应用程序上工作时,我遇到了一个我没想过或以前遇到过的行为。

考虑这个简单的类:

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:我确实尝试在发布之前进行搜索,但我真的不知道要搜索什么,所以我找不到任何有用的东西。

3 个答案:

答案 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)时间内。