views:

135

answers:

2

I have an object called FormObject that contains two ArrayLists - oldBooks and newBooks - both of which contain Book objects.

oldBooks is allowed to contain duplicate Book objects newBooks is not allowed to contain duplicate Book objects within itself and cannot include any duplicates of Book objects in the oldBooks list.

The definition of a duplicate Book is complex and I can't override the equals method as the definition is not universal across all uses of the Book object.

I plan to have a method on the FormObject class called removeDuplicateNewBooks which will perform the above functionality.

How would you go about implementing this? My first thought was to use HashSets to eliminate the duplicates but not being able to override equals on the Book object means it won't work.

+5  A: 

You can use a TreeSet with a custom Comparator<Book>:

  • construct the TreeSet with a Comparator implementing the custom logic you want
  • use set.addAll(bookList)

Now the Set contains only unique books.

Bozho
I thought about this, but it felt 'wrong' to use a comparator as semantically they're supposed to be used for sorting objects, not testing an alternative definition of equality..
Annie
True, but the contract is that a comparator returns 0 if and only if the objects are equal, so it implies equality testing.
seanizer
Yeah, that's true. This is definitely the most elegant solution.
Annie
+3  A: 

For making the new books unique:

Create a wrapper class around Book and declare it's equals / hashCode methods based on the enclosed book object:

public class Wrapper{

    private final Book book;

    public Wrapper(final Book book){
        assert book != null;
        this.book = book;
    }

    public Book getBook(){
        return this.book;
    }

    @Override
    public boolean equals(final Object other){
        return other instanceof Wrapper ? 
            Arrays.equals(
                this.getBookInfo(),
                ((Wrapper) other).getBookInfo()
            ) : false;
    }

    @Override
    public int hashCode(){
        return Arrays.hashCode(this.getBookInfo());
    }

    private String[] getBookInfo(){
        return new String[] { 
            this.book.getAuthor(), 
            this.book.getTitle(), 
            this.book.getIsbn() 
        };
    }

}

EDIT: Optimized equals and hashCode and fixed a bug in hashCode.

Now use a set to remove duplicates:

Set<Wrapper> wrappers = new HashSet<Wrapper>();
for(Book book: newBooks){
    wrappers.add(new Wrapper(book);
}
newBooks.clear();
for(Wrapper wrapper: wrappers){
    newBooks.add(wrapper.getBook());
}

(But of course the TreeSet answer with the custom comparator is more elegant because you can use the Book class itself)

EDIT: (removed reference to apache commons because my improved equals / hashCode methods are better)

seanizer
Hmm, this is an interesting solution, definitely not something I would have thought of.
Annie
good object-oriented solution.
Bozho