![](/img/trans.png)
[英]How to remove the StringBuilder object from an ArrayList?
[英]In Java, How to remove duplication from an ArrayList<StringBuilder> efficiently?
我試圖使用HashSet
從ArrayList<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);
這行似乎是邏輯錯誤。 您將添加一個已經在set
的String
並將其添加到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
不會覆蓋hashCode
和equals
,因此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.