簡體   English   中英

列表的異常行為 <T> 在jAVA中

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM