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