如何删除List <myobject>中没有equals / hashcode的重复对象?</myobject>

时间:2011-07-13 14:02:59

标签: java collections

我必须删除List中的重复对象。 它是来自对象博客的列表,如下所示:

public class Blog {
    private String title;
    private String author;
    private String url;
    private String description;
    ...
}

重复对象是一个对象,其标题,作者,网址和描述等于其他对象。

我不能改变这个对象。我不能把新的方法放在上面。

我该怎么做?

21 个答案:

答案 0 :(得分:16)

以下是适用于此场景的完整代码:

    class Blog {
private String title;
private String author;
private String url;
public String getTitle() {
    return title;
}

public void setTitle(String title) {
    this.title = title;
}

public String getAuthor() {
    return author;
}

public void setAuthor(String author) {
    this.author = author;
}

public String getUrl() {
    return url;
}

public void setUrl(String url) {
    this.url = url;
}

public String getDescription() {
    return description;
}

public void setDescription(String description) {
    this.description = description;
}

private String description;    

Blog(String title, String author, String url, String description)
{
    this.title = title;
    this.author = author;
    this.url = url;
    this.description = description; 
}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    if(obj instanceof Blog)
    {
        Blog temp = (Blog) obj;
        if(this.title == temp.title && this.author== temp.author && this.url == temp.url && this.description == temp.description)
            return true;
    }
    return false;

}
@Override
public int hashCode() {
    // TODO Auto-generated method stub

    return (this.title.hashCode() + this.author.hashCode() + this.url.hashCode() + this.description.hashCode());        
}

}

这是消除重复的主要功能:

    public static void main(String[] args) {
    Blog b1 = new Blog("A", "sam", "a", "desc");
    Blog b2 = new Blog("B", "ram", "b", "desc");
    Blog b3 = new Blog("C", "cam", "c", "desc");
    Blog b4 = new Blog("A", "sam", "a", "desc");
    Blog b5 = new Blog("D", "dam", "d", "desc");
    List<Blog> list = new ArrayList();
    list.add(b1);
    list.add(b2);
    list.add(b3);
    list.add(b4);       
    list.add(b5);

    //Removing Duplicates;
    Set<Blog> s= new HashSet<Blog>();
    s.addAll(list);         
    list = new ArrayList<Blog>();
    list.addAll(s);        
    //Now the List has only the identical Elements

}

答案 1 :(得分:11)

如果你不能编辑类的来源(为什么不呢?),那么你需要迭代列表并根据提到的四个标准(“标题,作者,网址和描述”)比较每个项目。

要以高效的方式执行此操作,我会创建一个新类,例如BlogKey包含这四个元素,正确实现equals()hashCode() 。然后,您可以遍历原始列表,为每个列表构建BlogKey并添加到HashMap

Map<BlogKey, Blog> map = new HashMap<BlogKey, Blog>();
for (Blog blog : blogs) {
     BlogKey key = createKey(blog);
     if (!map.containsKey(key)) {
          map.put(key, blog);
     }
}
Collection<Blog> uniqueBlogs = map.values();

但最简单的方法是编辑Blog的原始源代码,以便正确实现equals()hashCode()

答案 2 :(得分:10)

确保Blog已定义方法equals(Object)hashCode(),然后addAll(list)new HashSet()new LinkedHashSet(),如果订单为重要的。

更好的是,从一开始就使用Set而不是List,因为您显然不需要重复,最好是您的数据模型反映出来而不是必须在事实。

答案 3 :(得分:4)

  1. 使用这4个字段覆盖hashCode()equals(..)
  2. 使用new HashSet<Blog>(blogList) - 这将为您提供Set,根据定义没有重复项
  3. 更新:由于您无法更改类,因此这是一个O(n ^ 2)解决方案:

    • 创建新列表
    • 迭代第一个列表
    • 在内部循环中迭代第二个列表并验证它是否具有相同字段的元素

    如果您使用外部化HashSethashCode()方法提供equals(..)数据结构,则可以提高效率。

答案 4 :(得分:4)

使用set:

yourList = new ArrayList<Blog>(new LinkedHashSet<Blog>(yourList));

这将创建没有重复的列表,元素顺序将与原始列表中一样。

请不要忘记为您的班级Blog实现hashCode()和equals()。

答案 5 :(得分:2)

以下是删除重复对象的一种方法。

博客类应该像这样或类似的东西,比如适当的pojo

public class Blog {

    private String title;
    private String author;
    private String url;
    private String description;

    private int hashCode;



    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
       this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public boolean equals(Object obj) {

        Blog blog = (Blog)obj;

        if(title.equals(blog.title) &&
                author.equals(blog.author) &&
                url.equals(blog.url) &&
                description.equals(blog.description))
        {
            hashCode = blog.hashCode;
            return true;
        }else{
            hashCode = super.hashCode();
            return false;
        }
    }

}

并像这样使用它来删除重复的对象。这里的关键数据结构是Set和LinkedHashSet。它将删除重复项并保持条目顺序

    Blog blog1 = new Blog();
    blog1.setTitle("Game of Thrones");
    blog1.setAuthor("HBO");
    blog1.setDescription("The best TV show in the US");
    blog1.setUrl("www.hbonow.com/gameofthrones");

    Blog blog2 = new Blog();
    blog2.setTitle("Game of Thrones");
    blog2.setAuthor("HBO");
    blog2.setDescription("The best TV show in the US");
    blog2.setUrl("www.hbonow.com/gameofthrones");

    Blog blog3 = new Blog();
    blog3.setTitle("Ray Donovan");
    blog3.setAuthor("Showtime");
    blog3.setDescription("The second best TV show in the US");
    blog3.setUrl("www.showtime.com/raydonovan");

    ArrayList<Blog> listOfBlogs = new ArrayList<>();

    listOfBlogs.add(blog1);
    listOfBlogs.add(blog2);
    listOfBlogs.add(blog3);


    Set<Blog> setOfBlogs = new LinkedHashSet<>(listOfBlogs);

    listOfBlogs.clear();
    listOfBlogs.addAll(setOfBlogs);

    for(int i=0;i<listOfBlogs.size();i++)
        System.out.println(listOfBlogs.get(i).getTitle());

运行它应该打印

Game of Thrones
Ray Donovan

第二个将被删除,因为它是第一个对象的副本。

答案 6 :(得分:1)

您可以使用distinct删除重复项

List<Blog> blogList = ....// add your list here

blogList.stream().distinct().collect(Collectors.toList());

答案 7 :(得分:1)

您可以使用标题,作者,网址和说明覆盖equals()方法。 (和hashCode(),因为如果你覆盖一个,你应该覆盖另一个)。然后使用HashSet类型的<blog>

答案 8 :(得分:1)

您需要的第一步是实现equals方法并比较您的字段。之后,步骤会有所不同。

您可以使用:if(!list2.contains(item))创建一个新的空列表并循环遍历原始列表,然后执行添加。

另一种快速的方法是将它们全部塞进一个Set并将它们拉回到List中。这是有效的,因为集合不允许重复开始。

答案 9 :(得分:1)

  

我不能改变这个对象。我不能把新的方法放在上面。   
  
我该怎么做?

如果你还意味着我如何使对象不可变并阻止子类化:使用final关键字

public final class Blog { //final classes can't be extended/subclassed
   private final String title; //final members have to be set in the constructor and can't be changed
   private final String author;
   private final String url;
   private final String description;
    ...
}

编辑:我刚刚看到你的一些评论,似乎你想改变课程但不能(第三方我假设)。

为了防止重复,您可以使用实现适当的equals()hashCode()的包装器,然后使用其他人提到的Set方法:

 class BlogWrapper {
   private Blog blog; //set via constructor etc.

   public int hashCode() {
     int hashCode = blog.getTitle().hashCode(); //check for null etc.
     //add the other hash codes as well
     return hashCode;
   }

   public boolean equals(Object other) {
     //check if both are BlogWrappers
     //remember to check for null too!
     Blog otherBlog = ((BlogWrapper)other).getBlog(); 
     if( !blog.getTitle().equals(otherBlog.getTitle()) {
       return false;
     }
     ... //check other fields as well
     return true
   }
 }

请注意,这只是一个粗略而简单的版本,不包含必须的空检查。

最后使用Set<BlogWrapper>,遍历所有博客并尝试将new BlogWrapper(blog)添加到该集合中。最后,您应该只在集合中拥有唯一(包装)博客。

答案 10 :(得分:1)

如果您的Blog班级上定义了适当的equals()方法,最简单的方法就是从列表中创建Set,这会自动删除重复项:

List<Blog> blogList = ...; // your initial list
Set<Blog> noDups = new HashSet<Blog>(blogList)

这可能会透明地与其余代码一起工作 - 例如,如果您只是迭代内容,那么Collection的任何实例都可以与另一个实例一样好。 (如果迭代顺序很重要,那么您可能更喜欢使用LinkedHashSet,这将保留列表的原始顺序。)

如果你真的需要结果是List然后保持简单的方法,你可以通过包裹ArrayList(或类似的)再次直接转换它。如果你的藏品相对较小(比如说不到一千个元素),那么这种方法的明显效率低下可能并不重要。

答案 11 :(得分:1)

我尝试了几种从java对象列表中删除重复项的方法
其中一些是:1。覆盖equals和hashCode方法,并通过将列表传递给set类构造函数将列表转换为集合,然后删除并添加全部.2。运行2指针并通过运行2 for循环手动删除副本一个在另一个内部,就像我们在C语言中为数组所做的那样.3。为bean编写一个匿名的Comparator类并执行Collections.sort,然后运行2个指向向前移动。



而且我的要求更多的是从近500万个物体中移除近100万个重复物。
因此经过这么多试验后,我得到了第三种选择,我觉得这是最有效和最有效的方法,结果证明是评估在几秒钟内,其他2个选项几乎需要10到15分钟。
第一和第二选项非常无效,因为当我的对象增加了删除重复项所需的时间以指数方式增加。

所以最后第三个选项是最好的。

答案 12 :(得分:1)

    import java.util.ArrayList;

    import java.util.HashSet;

    class Person

{
    public int age;
    public String name;
    public int hashCode()
    {
       // System.out.println("In hashcode");
        int hashcode = 0;
        hashcode = age*20;
        hashcode += name.hashCode();
        System.out.println("In hashcode :  "+hashcode);
        return hashcode;
    }
     public boolean equals(Object obj)
        {
            if (obj instanceof Person)
                {
                    Person pp = (Person) obj;
                    boolean flag=(pp.name.equals(this.name) && pp.age == this.age); 
                    System.out.println(pp);
                    System.out.println(pp.name+"    "+this.name);
                    System.out.println(pp.age+"    "+this.age);
                    System.out.println("In equals : "+flag);
                    return flag;
                }
                else 
                    {
                    System.out.println("In equals : false");
                        return false;
                    }
         }
    public void setAge(int age)
    {
        this.age=age;
    }
    public int getAge()
    {
        return age;
    }
    public void setName(String name )
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
    public String toString()
    {
        return "[ "+name+", "+age+" ]";
    }
}
class ListRemoveDuplicateObject 
{
    public static void main(String[] args) 
    {
        ArrayList<Person> al=new ArrayList();

            Person person =new Person();
            person.setName("Neelesh");
            person.setAge(26);
            al.add(person);

            person =new Person();
            person.setName("Hitesh");
            person.setAge(16);
            al.add(person);

            person =new Person();
            person.setName("jyoti");
            person.setAge(27);
            al.add(person);

            person =new Person();
            person.setName("Neelesh");
            person.setAge(60);
            al.add(person);

            person =new Person();
            person.setName("Hitesh");
            person.setAge(16);
            al.add(person);

            person =new Person();
            person.setName("Mohan");
            person.setAge(56);
            al.add(person);

            person =new Person();
            person.setName("Hitesh");
            person.setAge(16);
            al.add(person);

        System.out.println(al);
        HashSet<Person> al1=new HashSet();
        al1.addAll(al);
        al.clear();
        al.addAll(al1);
        System.out.println(al);
    }
}

输出

[[Neelesh,26],[Hitesh,16],[jyoti,27],[Neelesh,60],[Hitesh,16],[Mohan,56],[Hitesh,16]]
在哈希码中:-801018364
在哈希码中:-2133141913
在哈希码中:101608849
在哈希码中:-801017684
在哈希码中:-2133141913
[Hitesh,16]
Hitesh Hitesh
16 16
等于:真实 在哈希码:74522099
在哈希码中:-2133141913
[Hitesh,16]
Hitesh Hitesh
16 16
等于:真实 [[Neelesh,60],[Neelesh,26],[Mohan,56],[jyoti,27],[Hitesh,16]]

答案 13 :(得分:1)

使用此代码

 public List<Blog> removeDuplicates(List<Blog> list) {
    // Set set1 = new LinkedHashSet(list);
    Set set = new TreeSet(new Comparator() {

        @Override
        public int compare(Object o1, Object o2) {
            if (((Blog) o1).get().equalsIgnoreCase(((Blog) o2).getId()) /*&&
                    ((Blog)o1).getName().equalsIgnoreCase(((Blog)o2).getName())*/) {
                return 0;
            }
            return 1;
        }
    });
    set.addAll(list);

    final List newList = new ArrayList(set);
    return newList;
}

答案 14 :(得分:1)

这可以使用属性从逻辑上解决。这里有一个称为键的属性。

  1. 取出对象中的所有String属性并将其放在列表中。
  2. 在属性包含的列表天气中检查(如果有),然后将其删除。
  3. 返回对象列表。

List<Object> objectList = new ArrayList<>();
 List<String> keyList = new ArrayList<>();
  objectList.forEach( obj -> {
   if(keyList.contains(unAvailabilityModel.getKey())) 
         objectList.remove(unAvailabilityModel); 
    else
        keyList.add(unAvailabilityModel.getKey();
});
return objectList;

答案 15 :(得分:0)

最简单,最有效的方法是允许eclipse生成并覆盖equals和hashcode方法。只需在提示时选择要检查重复项的属性,您应该全部设置。

此外,一旦列表准备就绪,将其放入一个集合中,你就可以复制它了。

答案 16 :(得分:0)

如果由于某些原因您不想覆盖equals方法,而又想基于多个属性删除重复项,那么我们可以创建一个通用方法来做到这一点。

我们可以编写2个版本:

1。修改原始列表:

@SafeVarargs
public static <T> void removeDuplicatesFromList(List<T> list, Function<T, ?>... keyFunctions) {

    Set<List<?>> set = new HashSet<>();

    ListIterator<T> iter = list.listIterator();
    while(iter.hasNext()) {
        T element = iter.next();

        List<?> functionResults = Arrays.stream(keyFunctions)
                .map(function -> function.apply(element))
                .collect(Collectors.toList());

        if(!set.add(functionResults)) {
            iter.remove();
        }
    }
}

2。返回新列表:

@SafeVarargs
public static <T> List<T> getListWithoutDuplicates(List<T> list, Function<T, ?>... keyFunctions) {

    List<T> result = new ArrayList<>();

    Set<List<?>> set = new HashSet<>();

    for(T element : list) {
        List<?> functionResults = Arrays.stream(keyFunctions)
                .map(function -> function.apply(element))
                .collect(Collectors.toList());

        if(set.add(functionResults)) {
            result.add(element);
        }
    }

    return result;
}

在两种情况下,我们都可以考虑任意数量的属性。

例如,要基于4个属性titleauthorurldescription删除重复项:

removeDuplicatesFromList(blogs, Blog::getTitle, Blog::getAuthor, Blog::getUrl, Blog::getDescription);

该方法通过利用equals的{​​{1}}方法来工作,该方法将检查其元素的相等性。在我们的例子中,List的元素是从传递的getter中获取的值,我们可以将该列表用作functionResults的元素来检查重复项。

完整示例:

Set

答案 17 :(得分:0)

我们还可以使用Comparator检查重复的元素。示例代码如下,

私有布尔checkDuplicate(列出学生DTO){

2909

答案 18 :(得分:0)

建议重写equals()hashCode()以使用基于哈希的集合,包括HashMapHashSetHashtable,因此您可以通过使用博客列表启动HashSet对象来轻松删除重复项。

List<Blog> blogList = getBlogList();
Set<Blog> noDuplication = new HashSet<Blog>(blogList);

但是,由于Java 8具有非常干净的版本来执行此操作,正如您提到的那样,您无法更改代码来添加equals()hashCode()

Collection<Blog> uniqueBlogs = getUniqueBlogList(blogList);

private Collection<Blog> getUniqueBlogList(List<Blog> blogList) {
    return blogList.stream()
            .collect(Collectors.toMap(createUniqueKey(), Function.identity(), (blog1, blog2) -> blog1))
            .values();
}
List<Blog> updatedBlogList = new ArrayList<>(uniqueBlogs);

Collectors.toMap()的第三个参数是merge Function(功能接口),用于解决与同一键关联的值之间的冲突。

答案 19 :(得分:0)

创建一个包装Blog对象的新类,并提供所需的相等/哈希码方法。为了获得最大效率,我将在包装器上添加两个静态方法,一个用于转换博客列表 - &gt; Blog Wrapper列表和另一个转换Blog Wrapper列表 - &gt;博客列表。然后你会:

  1. 将您的博客列表转换为博客包装列表
  2. 将博客包装列表添加到哈希集
  3. 从哈希集中取回精简的博客包装列表
  4. 将博客包装列表转换为博客列表
  5. Blog Wrapper的代码将是这样的:

    import java.util.ArrayList;
    import java.util.List;
    
    public class BlogWrapper {
        public static List<Blog> unwrappedList(List<BlogWrapper> blogWrapperList) {
            if (blogWrapperList == null)
                return new ArrayList<Blog>(0);
    
            List<Blog> blogList = new ArrayList<Blog>(blogWrapperList.size());
            for (BlogWrapper bW : blogWrapperList) {
                blogList.add(bW.getBlog());
            }
    
            return blogList;
        }
    
        public static List<BlogWrapper> wrappedList(List<Blog> blogList) {
            if (blogList == null)
                return new ArrayList<BlogWrapper>(0);
    
            List<BlogWrapper> blogWrapperList = new ArrayList<BlogWrapper>(blogList
                    .size());
            for (Blog b : blogList) {
                blogWrapperList.add(new BlogWrapper(b));
            }
    
            return blogWrapperList;
        }
    
        private Blog blog = null;
    
        public BlogWrapper() {
            super();
        }
    
        public BlogWrapper(Blog aBlog) {
            super();
            setBlog(aBlog);
        }
    
        public boolean equals(Object other) {
            // Your equality logic here
            return super.equals(other);
        }
    
        public Blog getBlog() {
            return blog;
        }
    
        public int hashCode() {
            // Your hashcode logic here
            return super.hashCode();
        }
    
        public void setBlog(Blog blog) {
            this.blog = blog;
        }
    }
    

    你可以像这样使用它:

    List<BlogWrapper> myBlogWrappers = BlogWrapper.wrappedList(your blog list here);
    Set<BlogWrapper> noDupWrapSet = new HashSet<BlogWrapper>(myBlogWrappers);
    List<BlogWrapper> noDupWrapList = new ArrayList<BlogWrapper>(noDupSet);
    List<Blog> noDupList = BlogWrapper.unwrappedList(noDupWrapList);
    

    很明显,您可以使上述代码更有效,特别是通过使Blog Wrapper上的wrap和unwrap方法采用集合而不是列表。

    包装Blog类的另一种方法是使用像BCEL这样的字节码操作库来实际更改Blog的equals和hashcode方法。但是,当然,如果它们需要原始的equals / hashcode行为,那么这可能会给代码的其余部分带来意想不到的后果。

答案 20 :(得分:0)

首先覆盖equals()方法:

@Override
public boolean equals(Object obj)
{
    if(obj == null) return false;
    else if(obj instanceof MyObject && getTitle() == obj.getTitle() && getAuthor() == obj.getAuthor() && getURL() == obj.getURL() && getDescription() == obj.getDescription()) return true;
    else return false;
}

然后使用:

List<MyObject> list = new ArrayList<MyObject>;
for(MyObject obj1 : list)
{
    for(MyObject obj2 : list)
    {
        if(obj1.equals(obj2)) list.remove(obj1); // or list.remove(obj2);
    }
}