簡體   English   中英

在Java中,如何從ArrayList中刪除重復項 <StringBuilder> 有效率的?

[英]In Java, How to remove duplication from an ArrayList<StringBuilder> efficiently?

我試圖使用HashSetArrayList<StringBuilder>刪除重復項。

例如,這是一個ArrayList ,每一行都是一個StringBuilder對象。

"u12e5 u13a1 u1423"
"u145d"
"u12e5 u13a1 u1423"
"u3ab4 u1489"

我想得到以下內容:

"u12e5 u13a1 u1423"
"u145d"
"u3ab4 u1489"

我當前的實現是:

static void removeDuplication(ArrayList<StringBuilder> directCallList) {
    HashSet<StringBuilder> set = new HashSet<StringBuilder>();
    for(int i=0; i<directCallList.size()-1; i++) {
        if(set.contains(directCallList.get(i)) == false)
            set.add(directCallList.get(i));
    }   
    StringBuilder lastString = directCallList.get(directCallList.size()-1);
    directCallList.clear();
    directCallList.addAll(set);
    directCallList.add(lastString);
} 

但是隨着ArrayList大小的增加,性能會越來越差。 這個實現有什么問題嗎? 還是在性能方面有更好的選擇?

StringBuilder不實現equals()或hashcode()。 如果兩個StringBuilder是完全相同的對象,則它們是相等的,因此將它們添加到HashSet不會排除內容相同的兩個不同的StringBuilder對象。

您應該將StringBuilders轉換為String對象。

同樣,您應該在構造函數中使用“初始容量”初始化HashSet。 如果您要處理大量對象,這將有助於提高速度。

最后,在添加對象之前不必在哈希集中調用contains()。 只需將您的字符串添加到集合中,集合將拒絕重復項(並返回false)。

讓我們分析您的方法以找到可以改進的地方:

static void removeDuplication(ArrayList<StringBuilder> directCallList) {
    HashSet<StringBuilder> set = new HashSet<StringBuilder>();
    for(int i=0; i<directCallList.size()-1; i++) {
        if(set.contains(directCallList.get(i)) == false)
            set.add(directCallList.get(i));
    }

對於ArrayList每個元素,此for循環重復一次。 對於手頭的任務來說,這似乎是不可避免的。 但是,由於HashSet只能包含每個項之一,因此if語句是多余的。 HashSet.add()再次執行完全相同的檢查。

    StringBuilder lastString = directCallList.get(directCallList.size()-1);

我不明白需要從您的列表中獲取lastString然后添加它。 如果您的循環正常工作,則應該已經將其添加到HashSet

    directCallList.clear();

根據列表的實現,這可能需要O(n)時間,因為它可能需要訪問列表中的每個元素。

    directCallList.addAll(set);

同樣,這需要O(n)時間。 如果沒有重復項,則set包含原始項。

    directCallList.add(lastString);

這行似乎是邏輯錯誤。 您將添加一個已經在setString並將其添加到directCallList }

因此,總的來說,該算法花費O(n)時間,但常數為3 如果可以減少此因素,則可以提高性能。 一種方法是簡單地創建一個新的ArrayList ,而不是清除現有的ArrayList

此外,如果您使用正確的構造函數並返回不包含重復項的ArrayList則可以在一行中編寫此removeDuplication()函數:

static List<StringBuilder> removeDuplication(List<StringBuilder> inList) {
    return new ArrayList<StringBuilder>(new HashSet<StringBuilder>(inList));
}

當然,這仍然不能解決其他人指出的StringBuilder問題。

因此,您還有其他選擇,但是我喜歡我的解決方案簡短,簡單,切合實際。 我將您的方法更改為不再操縱參數,而是返回一個新的List 我使用Set<String>來查看每個StringBuilder的內容是否已經包含在內並返回唯一的String 我還為每個循環使用了,而不是按索引訪問。

static List<StringBuilder> removeDuplication(List<StringBuilder> directCallList) {
    HashSet<String> set = new HashSet<String>();
    List<StringBuilder> returnList = new ArrayList<StringBuilder>();
    for(StringBuilder builder : directCallList) {
        if(set.add(builder.toString())
            returnList.add(builder);
    }   
    return returnList;
} 

如Sam所述, StringBuider不會覆蓋hashCodeequals ,因此Set將無法正常工作。

我認為答案是將Builder封裝在只執行一次toString的對象中:

class Wrapper{
   final String string;
   final StringBuilder builder;

   Wrapper(StringBuilder builder){
      this.builder = builder;
      this.string = builder.toString();
   }

   public int hashCode(){return string.hashCode();}

   public boolean equals(Object o){return string.equals(o);}
}     


 public Set removeDups(List<StringBuilder> list){
    Set<Wrapper> set = ...;
    for (StringBuilder builder : list)
       set.add(new Wrapper(builder));

    return set;
 }

可以更新removeDups方法以從集合中提取構建器,並返回List<StringBuilder>

如前所述,StringBuilders不會覆蓋Object#equals ,也不是Comparable

盡管使用StringBuilders串聯字符串是stringBuilder.toString()的方法,但我建議您完成串聯后, 在列表中存儲基礎字符串stringBuilder.toString() )而不是StringBuilders。

刪除重復項然后變成一行:

Set<String> set = new HashSet<String>(list);

甚至更好的是,如果您不需要知道重復項,則將字符串直接存儲在集合中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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