有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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>的某些默认行为有关

话虽如此,这种意外行为的原因可能是什么

附言:我在发帖前确实尝试过搜索,但我真的不知道要搜索什么,所以我没有找到任何有用的东西


共 (3) 个答案

  1. # 1 楼答案

    第二种方法的结果是,在每个对象的列表中总是有一个长值(即使应该有更多)

    问题

    A mA = new A(id, date); if(!mA.list.contains(mLong))

    正如您所见,您没有从mList获取类A的引用,您正在检查刚刚创建的列表中是否包含long值,该列表只会添加一个。所以基本上你要做的是创建一个新的a类,在long列表中有1个long值,并添加到mList中

    另一方面,您的第一种方法是获取已经添加的类A的实例,并检查该long是否包含在列表中,如果没有,则将其添加到long列表中

  2. # 2 楼答案

    这是因为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;
    }
    
  3. # 3 楼答案

    如果有一个方法mList.addIfAbsent(mA)(在将其添加到列表后返回mA,或者返回已经存在并与equals上的mA匹配的对象),它将使您的操作变得非常简单

    mA = mList.addIfAbsent(mA);
    mA.list.addIfAbsent(mLong);
    

    在第二个例子中,当mA等价物已经存在时,显然打破了这种机制。基本上,您可以将addIfAbsent(mA)的定义更改为“如果列表中没有其他对象与之相等,则将mA添加到列表中,并返回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);
    

    这不会降低big-O的复杂度,但至少只需要一个O(n)操作(相比之下,工作代码中只有两个)

    顺便说一句,这对你和其他人来说可能是显而易见的;如果是这样的话,请原谅,但是如果这些列表大于数千个元素,如果您使用HashSet,或者如果您关心插入顺序,甚至可以使用LinkedHashSet,那么性能可能会显著提高。在这种情况下,您只需尝试add一个对象,如果它已经存在,则获取false,这只需要花费O(1)个时间。然后你将get(mA)从集合中,而不是你与indexOf的迂回方式,同样在O(1)时间内