[英]Unexpected behavior with List<T> in jAVA
在我的應用程序上工作時,遇到了我未曾預期或以前遇到的行為。
考慮這個簡單的類:
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無法按預期工作。
該代碼實際上是在循環中運行,並且有望在mList
內部存在各種類型為A
對象,並且每個對象的list
字段中都有數量未知(超過1個)的long
值。
真正發生的情況是,第一種方法可以正常工作,而第二種方法會導致以下情況:每個對象的list
中始終有一個long
值(即使應該有更多)。
我個人不知道是什么原因可能導致它那樣工作,這就是為什么我在這里,要為這個“謎”尋求答案。 我最瘋狂的猜測是,它與指針有關,或者可能是我不知道的List<T>
某些默認行為。
話雖這么說,這種意外行為的原因可能是什么?
PS:我確實嘗試過在發布前進行搜索,但是我真的不知道要搜索什么,因此沒有發現任何有用的信息。
這是因為mList.contains(mA)
通過調用o1.equals(o2)
在內部檢查對象的相等性。 equals()
的默認實現如下所示:
public boolean equals(Object o) {
return this == o;
}
顯然,實例並不相同,因此您每次都添加一個新實例。 覆蓋類A
equals()
可以解決此問題。 如果它們具有相同的ID,我猜實例是否相同?
public boolean equals(Object o) {
return this.mId == o.mId;
}
第二種方法導致每個對象的列表內始終有1個long值(即使應該有更多)。
問題
A mA = new A(id, date); if(!mA.list.contains(mLong))
如您所見,您沒有從mList獲取類A的引用,並且正在檢查剛創建的列表中long值是否包含,該值只會添加一個。 因此,基本上,您正在做的是在long列表上創建一個具有1個long值的A類新實例,並將其添加到mList中
另一方面,您的第一個方法是獲取已經添加的類A的實例,並檢查該長類是否包含在列表中(如果沒有),然后將其長添加到列表中。
如果有一個方法mList.addIfAbsent(mA)
(將其添加到列表后返回mA
,或者返回已經存在並與mA
equals
),它將使您的操作變得很簡單
mA = mList.addIfAbsent(mA);
mA.list.addIfAbsent(mLong);
在您的第二個示例中,很顯然,當mA
等效mA
已經存在時,您會打破這種機制。 基本上,將addIfAbsent(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);
這不會降低您的big-O復雜性,但至少可以僅執行一次O(n)操作(相比於您的工作代碼中的兩次操作)。
順便說一句,這對您和其他所有人可能都是顯而易見的; 如果是這樣,對不起,但是,如果這些列表的大小超過數千,那么如果您使用HashSet
,或者如果您關心插入順序,則甚至可以使用LinkedHashSet
,可以顯着提高性能。 在這種情況下,您將嘗試add
一個對象,如果該對象已經存在,則為false
,這只會花費O(1)時間。 然后,您將從集合中get(mA)
而不是使用indexOf
的環形交叉路,也在O(1)時間內。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.