在Java中,如何有效地从ArrayList <stringbuilder>中删除重复?</stringbuilder>

时间:2012-10-15 16:56:59

标签: java arrays arraylist stringbuilder duplicate-removal

我尝试使用HashSet删除ArrayList<StringBuilder>的重复项。

E.g。这是一个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大小的增长,性能变得越来越差。这个实现有什么问题吗?或者你在表现方面有更好的表现吗?

5 个答案:

答案 0 :(得分:9)

StringBuilder没有实现equals()或hashcode()。如果它们是完全相同的对象,则两个StringBuilder仅相等,因此将它们添加到HashSet将不会排除具有相同内容的两个不同的StringBuilder对象。

您应该将StringBuilders转换为String对象。

此外,您应该在构造函数中使用“初始容量”初始化HashSet。如果处理大量对象,这将有助于提高速度。

最后,在添加对象之前,没有必要在hashset上调用contains()。只需将你的字符串添加到集合中,集合将拒绝重复(并将返回false)。

答案 1 :(得分:2)

让我们分析一下你的方法,找出我们可以改进的地方:

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);

这一行似乎是一个逻辑错误。您将添加String已添加到set并添加到directCallList。     }

总体而言,此算法需要O(n)时间,但常量因子为3。如果可以减少此因素,则可以提高性能。一种方法是简单地创建一个新的ArrayList,而不是清除现有的removeDuplication()

此外,如果您使用正确的构造函数并且返回ArrayList而不重复,则可以在一行中写入此static List<StringBuilder> removeDuplication(List<StringBuilder> inList) { return new ArrayList<StringBuilder>(new HashSet<StringBuilder>(inList)); } 函数:

StringBuilder

当然,这仍然没有解决其他人指出的{{1}}问题。

答案 2 :(得分:1)

所以你有其他选择,但我喜欢我的解决方案简短,简单,重点突出。我已将您的方法更改为不再操作参数,而是返回新的List。我使用Set<String>查看是否已包含每个StringBuilder的内容并返回唯一的String。我还为每个循环使用了a而不是通过索引访问。

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;
} 

答案 3 :(得分:0)

正如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>

答案 4 :(得分:0)

正如所解释的,StringBuilders不会覆盖Object#equals而不是Comparable

虽然使用StringBuilders连接你的字符串是可行的方法,但我建议你完成连接后,你应该存储基础字符串stringBuilder.toString())而不是列表中的StringBuilders。

删除重复项然后变为一行:

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

或者甚至更好,如果您不需要知道重复项,则直接将字符串存储在集合中。