2011-07-13 22 views
16

我必須刪除列表中的重複對象。 此列表有對象博客是這樣的:如何刪除列表中的重複對象<MyObject>沒有equals/hashcode?

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

複製的對象是有標題,作者,URL和說明等同於其他對象的對象。

而且我不能改變對象。我不能在上面添加新的方法。

我該怎麼做?

+2

的equals方法已經重寫? – RMT

回答

10

如果你不能編輯類的源代碼(爲什麼不?),那麼你需要遍歷列表並根據提到的四個條件(「title,author,url和description」)來比較每個項目。

爲此在高性能的方式,我會創建一個新的類,像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()

+1

因爲它是由MyBatis生成的類,並且在數據庫更改時再次生成,那麼我無法改變這個類。 而我不能創建另一個班級,因爲我有更多的班級來做到這一點。 但我想我會使用地圖,但密鑰將是四個字段的連接。 謝謝! 非常有用!解決了我的問題! 再次感謝! :) –

+5

就個人而言,我會質疑生成這樣一個實體類的工具的效用,並沒有實現equals()或hashcode(),因爲它看起來像這個類會以很多其他方式打破 - 但YMMV 。 –

+0

使用http://www.mybatis.org/generator/apidocs/reference/org/mybatis/generator/plugins/EqualsHashCodePlugin.html –

4
  1. 覆蓋hashCode()equals(..)使用這4場
  2. 使用new HashSet<Blog>(blogList) - 這會給你一個Set其具有如下的定義沒有重複

更新:既然你不能改變類,這裏的爲O(n^2)的解決方案:

  • 創建一個新的列表
  • 迭代在一個內部循環的第一個列表
  • 遍歷第二列表,並驗證它是否具有相同的字段

元素可以使這更有效的,如果你提供一個HashSet數據結構外在hashCode()equals(..)方法。

+0

我不能編輯對象的類,我如何做到這一點而不改變對象? –

8

確保Blog有方法equals(Object)和定義hashCode(),並addAll(list)然後到new HashSet(),或new LinkedHashSet()如果順序很重要。

更好的是,從一開始就使用Set而不是List,因爲您顯然不想重複,所以最好是您的數據模型反映了這一點,而不是在事後才刪除它們。

+0

但我不能編輯類。 –

+0

http://www.mybatis.org/generator/apidocs/reference/org/mybatis/generator/plugins/EqualsHashCodePlugin.html –

1

如果您Blog類上定義一個適當的equals()方法,最簡單的方法就是建立一個Set你的列表中,它會自動刪除重複:

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

的機會,這將工作透明地與你的代碼的其餘部分 - 例如,如果你只是遍歷內容,那麼Collection的任何實例是一樣好。 (如果迭代順序很重要,那麼您可能更喜歡使用LinkedHashSet,這將保留列表的原始順序)。

如果你確實需要的結果是List然後保持直接的方法,你可以通過包裝在ArrayList(或類似的)中直接轉換回來。如果你的收藏相對較小(比如不到一千個元素),那麼這種方法的明顯低效率可能並不重要。

+0

我不能編輯對象的類,我怎麼做到這一點沒有改變的對象? –

4

使用set:

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

這將不重複創建列表和元素順序將在最初的名單。

只是不要忘記爲你的類Blog實現hashCode()和equals()。

+0

我無法編輯對象的類,我如何做到這一點而不用改變對象? –

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); 
    } 
} 
+1

這是非常低效的。 O(N^2)或更糟。更不用說不正確,因爲Arrays.deepEquals()將數組作爲參數。 –

+0

@Christoffer你是完全正確的,但這是我想到的第一件事。 –

+0

你不能用'=='比較字符串!如果我有另一個-1給我會。 –

1

您可以覆蓋equals()方法,與標題,作者,URL和描述。 (和hashCode(),因爲如果你覆蓋一個,你應該覆蓋另一個)。然後使用<blog>類型的HashSet

+0

我不能編輯對象的類,我如何做到這一點而不需要改變對象? –

1

您需要的第一步是實現equals方法並比較您的字段。之後,步驟會有所不同。

您可以創建一個新的空列表並循環使用if(!list2.contains(item)),然後執行添加。

另一種快速的方法是將它們全部塞進一個Set並將它們拉回List中。這是可行的,因爲集合不允許重複開頭。

+2

錯!可比較將不適用於Set(你可以嘗試自己)。對於一個集合,你需要'equals'和'hashcode'。 – Bohemian

+0

超前亞!修正它在我寫了它約30秒後!雖然好,但沒有什麼可以在無序收藏中比較。 –

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)添加到該集合。最後,您應該只在該集合中擁有唯一(包裝)的博客。

+0

沒有,對不起...我是說,我不得不刪除博客對象的名單上重複不添加方法equals和hashCode,因爲我不能改變這個方法。 –

+0

@Diego看看我的答案,我說提供一個你可以寫的包裝。這不會改變Blog類本身。 – Thomas

0

創建一個包裝您的部落格的對象,並提供你所需要的平等/ hashCode方法的新類。爲了獲得最大的效率,我想補充的包裝兩個靜態方法,一個轉換博客列表 - >博客包裝清單和其他轉換博客包裝列表 - >博客列表。那麼你會:

  1. 將您的博客列表博客包裝列表
  2. 您的博客包裝列表添加到哈希集合
  3. 獲取冷凝博客包裝列表背出哈希集合
  4. 的轉換的博客列表包裝到博客列表

代碼博客封裝會是這​​樣的:

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

很明顯,你可以讓上面的代碼更有效,特別是通過使包裝和解開的博客包裝以集合,而不是列出的方法。

到包裹博客類是使用字節碼操作庫像BCEL真正改變了equals和hashCode方法用於博客的替代路線。但是,當然,如果它們需要原始的equals/hashcode行爲,那麼可能會對其他代碼產生意想不到的後果。

9

下面是完整的代碼,適用於這樣的場景:

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 

}

+0

你用==比較字符串生成的MyBatis'equals'和'hashCode'。它應該是this.title.equals(temp.title)...... –

1

我嘗試了幾種方法從java對象列表中刪除重複項
其中有些是
1.重寫equals和hashCode方法,並通過將列表中集類的構造函數列表轉換爲一組,做刪除和添加所有
2.運行2個球,並通過運行2手動刪除重複項for循環在另一個內部就像我們使用的C語言數組
3.Write一個匿名比較類豆做的,做了Collections.sort,然後運行2個指針正向刪除。



而更多的在我的要求是從近500萬的對象刪除近100萬的重複。
因此,經過這麼多的試驗後,我結束了第三個選項,我覺得它是最有效和最有效的方法,結果是在幾秒鐘內評估,其他兩個選項幾乎需要10到15分鐘。
第一個和第二個選項非常無效,因爲當我的對象增加以指數方式刪除重複項時所需的時間。

所以最後第三個選項是最好的。

0

最容易和最有效的方法是讓eclipse生成並覆蓋equals和hashcode方法。當提示時只需選擇要檢查重複項的屬性,並且應該全部設置。

此外,一旦列表準備就緒,把它放入一個集合,你有重複消失。

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],[亞太區首席技術官Matt,16],[喬蒂,27],[Neelesh,60],[亞太區首席技術官Matt,16],[Mohan將56 ],[亞太區首席技術官Matt,16]]
在哈希碼:-801018364
在哈希碼:-2133141913
在哈希碼:101608849
在哈希碼:-801017684
在哈希碼:-2133141 913
[亞太區首席技術官Matt,16]
亞太區首席技術官Matt亞太區首席技術官Matt
在等於:真
在哈希碼:74522099
在哈希碼:-2133141913
[亞太區首席技術官Matt,16]
亞太區首席技術官Matt亞太區首席技術官Matt
在等於:真
[[Neelesh,60],[Neelesh,26],[Mohan將56],[喬蒂,27],[亞太區首席技術官Matt,16]]

+0

此代碼闡釋瞭如何實現的方式,將改變階級平等和哈希。問題堅持你不要改變班級。 – CandiedOrange

1

這是刪除重複對象的一種方法。

的博客類應該是這樣的或類似的,比如適當的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 

第二個將被刪除,因爲它是第一個對象的副本。

0

使用此代碼

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

比較器接口的compare()函數用於排序和刪除重複項。 您必須使用'equals'和'hashCode'函數來確定兩個對象是否相等。 –