'Why is a Map<T, Set<T>> in my ADT is showing an empty set but in class method the same instance variable is showing the correct value in Java Code?

I am new to Java, I am doing a MOOC course of MIT which is not offered anymore, but still im doing the archieve version to learn Java and OOP, so there is no forums help for this course. Have been stuck on one problem for some hours now, although I think I have isolated the problem but still can't seem to understand what to do to fix this bug. Any help would really be appreciated. I have this interface that I am trying to implement

    package library;

import java.util.List;
import java.util.Set;

/**
 * Library represents a mutable collection of books.  The library may have multiple copies of the same book.
 * At any time, a particular book copy may be either "available" (i.e. present in the library building)
 * or "checked out" (i.e. not in the building, in the possession of a borrower).
 */
public interface Library {

    /**
     * Buy a new copy of a book and add it to the library's collection.
     * @param book Book to buy
     * @return a new, good-condition copy of the book, which is now available in this library
     */
    public BookCopy buy(Book book);
    
    /**
     * Check out a copy of a book.
     * @param copy Copy to check out. Requires that the copy be available in this library.
     */
    public void checkout(BookCopy copy);
    
    /**
     * Check in a copy of a book, making it available again.
     * @param copy Copy to check in.  Requires that the copy be checked out of this library.
     */
    public void checkin(BookCopy copy);
    
    /**
     * Test whether a book copy is available in this library.
     * @param copy Book copy to test
     * @return true if and only if copy is available in this library
     */
    public boolean isAvailable(BookCopy copy);
    
    /**
     * Get all the copies of a book.
     * @param book Book to find
     * @return set of all copies of the book in this library's collection, both available and checked out.
     */
    public Set<BookCopy> allCopies(Book book);
    
    /**
     * Get all the available copies of a book.
     * @param book Book to find
     * @return set of all copies of the book that are available in this library.
     */
    public Set<BookCopy> availableCopies(Book book);
    
    /**
     * Search for books in this library's collection.
     * @param query search string
     * @return list of books in this library's collection (both available and checked out) 
     * whose title or author match the search string, ordered by decreasing amount  of match.
     * A book should appear at most once on the list. 
     * Keyword matching and ranking is underdetermined, but at the very least must support: 
     *     - exact matching of title and author: i.e., if a copy of a book is in the library's 
     *           collection, then find(book.getTitle()) and find(book.getAuthors().get(i)) 
     *           must include book among the results.
     *     - date ordering: if two matching books have the same title and author but different
     *           publication dates, then the newer book should appear earlier on the list. 
     */
    public List<Book> find(String query);
    
    /**
     * Declare a copy of a book as lost from the library.  A copy can be declared lost if it is stolen
     * without being checked out, or if a borrower checks it out but never returns it. 
     * @param copy BookCopy to declare lost.  Must have been previously returned from buy() on this library.
     */
    public void lose(BookCopy copy);



}

here is the class thats implementing this interface

package library;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * BigLibrary represents a large collection of books that might be held by a city or
 * university library system -- millions of books.
 * 
 * In particular, every operation needs to run faster than linear time (as a function of the number of books
 * in the library).
 */
public class BigLibrary implements Library {

    // TODO: rep
    private final Map<Book, Set<BookCopy>> inLibrary;
    private final Map<Book, Set<BookCopy>> checkedOut;
    
    // TODO: rep invariant
    /*An intersection of collections of books inLibrary and checkout for a book would be an empty set provided there is atleast one book in the library
     * */
    
    // TODO: abstraction function
    /*The library represent a collection of books where each union of similar keys in 
     * inLibrary and checkout represent the collection of books, if the book key is not their the book is not in the library*/
    
    // TODO: safety from rep exposure argument
    /*Everything is private*/
    
    public BigLibrary() {
        //throw new RuntimeException("not implemented yet");
        this.inLibrary = new HashMap<>();
        this.checkedOut = new HashMap<>();
        
        this.checkRep();
    }
    
    // assert the rep invariant
    private void checkRep() {
        //throw new RuntimeException("not implemented yet");
        Set<Book> inLibraryKeys = this.inLibrary.keySet();
        Set<Book> checkOutKeys = this.checkedOut.keySet();
        
        if(!inLibraryKeys.isEmpty() && !checkOutKeys.isEmpty()) {
            Book firstBook = inLibraryKeys.iterator().next();
            if(checkOutKeys.contains(firstBook)) {
                //some of the copies have been checkedOut
                Set<BookCopy> intersection = this.inLibrary.get(firstBook);
                intersection.retainAll(this.checkedOut.get(firstBook));
                assert intersection.isEmpty() : "rep invariant voilated";
            }
        }
    }

    @Override
    public BookCopy buy(Book book) {
        //throw new RuntimeException("not implemented yet");
        BookCopy copyBook = new BookCopy(book);
        //get the keys to check if this books key is already there
        Set<Book> keys = this.getInLibraryKeys();
        if(keys.contains(book)) {
            this.inLibrary.get(book).add(copyBook);
        }
        else
        {
            Set<BookCopy> allCopiesSet = new HashSet<>();
            allCopiesSet.add(copyBook);
            this.inLibrary.put(book, allCopiesSet);
        }
        
        this.checkRep();
        return copyBook;
    }
    
    @Override
    public void checkout(BookCopy copy) {
        //throw new RuntimeException("not implemented yet");
        Book originalBook = copy.getBook();
        if(this.isAvailable(copy)) {
            //remove from library
            System.out.println("Value for key of inLibrary gotten in CHeckout" +this.inLibrary.get(copy.getBook()));
            this.inLibrary.get(copy.getBook()).remove(copy);
            System.out.println("Value for key of inLibrary after removal of a book in CHECKOUT" +this.inLibrary.get(copy.getBook()));
            //add to checkedout
            Set<Book> checkedOutKeys = this.getCheckedOutKeys();
            if(checkedOutKeys.contains(originalBook)) {
                Set<BookCopy> copiesOfOriginalBook = this.checkedOut.get(originalBook);
                copiesOfOriginalBook.add(copy);
            }
            else
            {
                //the book is been checked out for the first time
                Set<BookCopy> checkedOutBookSetForOriginalBook = new HashSet<>();
                checkedOutBookSetForOriginalBook.add(copy);
                this.checkedOut.put(originalBook, checkedOutBookSetForOriginalBook);
            }
        }
        
        this.checkRep();
    }
    
   
    
    @Override
    public void checkin(BookCopy copy) {
        //throw new RuntimeException("not implemented yet");
        Book originalBook = copy.getBook();
        Set<Book> checkedOutKeys = this.getCheckedOutKeys();
        System.out.println("checkedOutKeys.contains(originalBook): " +checkedOutKeys.contains(originalBook));
        if(checkedOutKeys.contains(originalBook)) {
            //remove from checkedout
//          System.out.println("IN FUNCTION CHECKED OUT BEFORE REMOVE: " +this.checkedOut);
//          System.out.println("IN FUNCTION INLIBRARY BEFORE REMOVE: " +this.inLibrary);
//          System.out.println("IN FUNCTION INLIBRARY SIZE: " +this.inLibrary.size());
            this.checkedOut.get(originalBook).remove(copy);
            System.out.println("IN FUNCTION CHECKED OUT AFTER REMOVE: " +this.checkedOut);
            
            //add the book back to the libraray
            Set<BookCopy> copiesForOriginalBookinLibrary = this.inLibrary.get(originalBook);
            copiesForOriginalBookinLibrary.add(copy);
//          System.out.println("BOOK'S SET" +copiesForOriginalBookinLibrary);
            
            
            this.inLibrary.put(originalBook, copiesForOriginalBookinLibrary);       
            System.out.println("INLIBRRY IN FUNCTION : " +this.inLibrary);
            //SSystem.out.println("INLIBRARY IN FUNCTION USING GETTER" + this.getInLibrary());
            System.out.println("Availability in Function:" +this.isAvailable(copy));
            //this.inLibrary.put(originalBook, copiesForOriginalBookinLibrary);
            //this.inLibrary.get(originalBook).add(copiesForOriginalBookinLibrary);
            
            
        }
        
        this.checkRep();
    }
    
    @Override
    public Set<BookCopy> allCopies(Book book) {
        throw new RuntimeException("not implemented yet");
    }

    @Override
    public Set<BookCopy> availableCopies(Book book) {
        throw new RuntimeException("not implemented yet");
    }
    
    @Override
    public boolean isAvailable(BookCopy copy) {
        //throw new RuntimeException("not implemented yet");
        Set<Book> inLibraryKeys = this.inLibrary.keySet();
        if(inLibraryKeys.contains(copy.getBook())) {
            Set<BookCopy> allCopies = this.inLibrary.get(copy.getBook());
            //return allCopies.contains(copy);
            if(allCopies.contains(copy))
            {
                return true;
            }
            else {
                return false;
            }
        }
        return false;
    }
    
    @Override
    public List<Book> find(String query) {
        throw new RuntimeException("not implemented yet");
    }
    
    @Override
    public void lose(BookCopy copy) {
        throw new RuntimeException("not implemented yet");
    }
    
   
    public static void main(String []args) {
        BigLibrary library = new BigLibrary();
        
        Book book = new Book("Test title", Arrays.asList("Furrukh", "Jamal"), 1985);
        BookCopy libraryBook = library.buy(book);
        BookCopy libraryBook2 = library.buy(book);
        System.out.println("inLibrary:" +library.getInLibrary());
        System.out.println("CheckedOut:" +library.getcheckedOut());
        
        Set<Book> keys = library.getInLibraryKeys();
        for(Book Book : keys) {
            //System.out.println("Book: " +Book);
        }
        
        library.checkout(libraryBook);
        
        
        System.out.println("inLibrary after checkout using .inLibrary" +library.inLibrary);
        System.out.println("inLibrary after checkout:" +library.getInLibrary());
        System.out.println("CheckedOut after checkout:" +library.getcheckedOut());
        
        library.checkin(libraryBook);
        
        System.out.println("inLibrary after checkin:" +library.getInLibrary() );
        System.out.println("CheckedOut after checkin:" +library.getcheckedOut() );
        //assertTrue("The library book is available again", library.isAvailable(libraryBook));
        System.out.println("Availability in main:" + library.isAvailable(libraryBook));
    }
    
    public Set<Book> getInLibraryKeys() {
        return this.inLibrary.keySet();
    }
    
    public Set<Book> getCheckedOutKeys(){
        return this.checkedOut.keySet();
    }
    
    public Map<Book, Set<BookCopy>> getInLibrary(){
        Map<Book, Set<BookCopy>> inLibrary = new HashMap<>();
        inLibrary.putAll(this.inLibrary);
        return inLibrary;
    }
    
    public Map<Book, Set<BookCopy>> getcheckedOut(){
        return this.checkedOut;
    }

    // uncomment the following methods if you need to implement equals and hashCode,
    // or delete them if you don't
    // @Override
    // public boolean equals(Object that) {
    //     throw new RuntimeException("not implemented yet");
    // }
    // 
    // @Override
    // public int hashCode() {
    //     throw new RuntimeException("not implemented yet");
    // }



}

here is what I am getting with all the prints on the console

    Equals hitting
inLibrary:{Title: Test title
Authors: Furrukh Jamal
Year : 1985=[Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good, Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good]}
CheckedOut:{}
Value for key of inLibrary gotten in CHeckout[Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good, Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good]
Value for key of inLibrary after removal of a book in CHECKOUT[Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good]
Equals hitting
inLibrary after checkout using .inLibrary{Title: Test title
Authors: Furrukh Jamal
Year : 1985=[]}
inLibrary after checkout:{Title: Test title
Authors: Furrukh Jamal
Year : 1985=[]}
CheckedOut after checkout:{Title: Test title
Authors: Furrukh Jamal
Year : 1985=[Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good]}
checkedOutKeys.contains(originalBook): true
IN FUNCTION CHECKED OUT AFTER REMOVE: {Title: Test title
Authors: Furrukh Jamal
Year : 1985=[]}
INLIBRRY IN FUNCTION : {Title: Test title
Authors: Furrukh Jamal
Year : 1985=[Title: Test title
Authors: Furrukh Jamal
Year : 1985
 Condition: good]}
Availability in Function:true
inLibrary after checkin:{Title: Test title
Authors: Furrukh Jamal
Year : 1985=[]}
CheckedOut after checkin:{Title: Test title
Authors: Furrukh Jamal
Year : 1985=[]}
Availability in main:false

I had a test case that I was failing, implemented the same case in the main of this class to debug, The problem is in I think when the checkout function is hit when I try to remove a single element from the set thats in as a value for a key in inLibrary. Both methods the checkin and checkout methods are showing the desired number of elements in the value set for the particular key. But the value returned in the main is always an empty set. Cant seem to understand what I am doing wrong in here. Any help would really be appreciated

Update

here is the Book and BookCopy implementation as well

package library;

import java.util.ArrayList;
import java.util.List;

/**
 * Book is an immutable type representing an edition of a book -- not the physical object, 
 * but the combination of words and pictures that make up a book.  Each book is uniquely
 * identified by its title, author list, and publication year.  Alphabetic case and author 
 * order are significant, so a book written by "Fred" is different than a book written by "FRED".
 */
public class Book {

    // TODO: rep
    private final String title;
    private final List<String> authors;
    private final int year;
    
    // TODO: rep invariant
    /*
     * @param string:  must be a non empty with atleast one charater
     * @param authors: must be non empty and each name must contain atleast one character
     * @param year : a non negative number*/
    
    // TODO: abstraction function
    /*A book is uniquely identifed by its title*/
    
    
    // TODO: safety from rep exposure argument
    /*Each filed is private and final
     * for authors defensive copying is implemented
     * year is finat so it is immutable*/
    
    /**
     * Make a Book.
     * @param title Title of the book. Must contain at least one non-space character.
     * @param authors Names of the authors of the book.  Must have at least one name, and each name must contain 
     * at least one non-space character.
     * @param year Year when this edition was published in the conventional (Common Era) calendar.  Must be nonnegative. 
     */
    public Book(String title, List<String> authors, int year) {
        if(title == null || title.isEmpty()) {
            throw new IllegalArgumentException("Title is null or empty");
        }
        if (authors == null || authors.isEmpty()) {
            throw new IllegalArgumentException("Authors cannot be empty");
        }
        if(year <= 0) {
            throw new IllegalArgumentException("Year can not be negative");
        }
        
        this.title = title;
        this.authors = authors;
        this.year  = year;
        
    }
    
    // assert the rep invariant
    private void checkRep() {
        //throw new RuntimeException("not implemented yet");
        assert this.title != null && !this.title.isEmpty(): "Rep invariant violated";
        assert this.authors != null && !this.authors.isEmpty() : "Rep invariant violated";
        assert this.year > 0: "Rep invariant violated";
            
    }
    
    /**
     * @return the title of this book
     */
    public String getTitle() {
        //throw new RuntimeException("not implemented yet");
        return this.title;
    }
    
    /**
     * @return the authors of this book
     */
    public List<String> getAuthors() {
        //throw new RuntimeException("not implemented yet");
        List<String> copyAuthors = new ArrayList<>();
        copyAuthors.addAll(this.authors);
        return copyAuthors; 
        
    }

    /**
     * @return the year that this book was published
     */
    public int getYear() {
        //throw new RuntimeException("not implemented yet");
        return this.year;
    }

    /**
     * @return human-readable representation of this book that includes its title,
     *    authors, and publication year
     */
    public String toString() {
        //throw new RuntimeException("not implemented yet");
        String authors = "";
        for(String author : this.authors)
        {
            authors += " " + author;
        }
        String stringRepresentation = "Title: " + this.title + "\n" + "Authors:" + authors + "\n" + "Year : " + this.year;
        return stringRepresentation;
    }

//     uncomment the following methods if you need to implement equals and hashCode,
//     or delete them if you don't
     @Override
     public boolean equals(Object that) {
         if(!(that instanceof Book))
         {
             return false;
         }
         
         Book thatBook = (Book) that;
         return thatBook.getTitle().equals(this.getTitle());
         
     }
    // 
     @Override
     public int hashCode() {
         //throw new RuntimeException("not implemented yet");
         int result = 2;
         int code = 0;
         code += this.year;
         code+= this.getTitle().hashCode();
         for (String author : this.authors)
         {
             code += author.hashCode();
         }
         
         result = 37 * result + code;
         
         return result;
     }



}


package library;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * BookCopy is a mutable type representing a particular copy of a book that is held in a library's
 * collection.
 */
public class BookCopy {

    // TODO: rep
    private final String title;
    private final List<String> authors;
    private final int year;
    private final Book parentBook;
    private Condition condition;
    // TODO: rep invariant
    /*
     * @param string:  must be a non empty with atleast one charater
     * @param authors: must be non empty and each name must contain atleast one character
     * @param year : a non negative number*/
    
    // TODO: abstraction function
    /*A book copy is a book uniquely identified by its title and can have two states*/ 
    
    // TODO: safety from rep exposure argument
    
    public static enum Condition {
        GOOD, DAMAGED
    };
    
    /**
     * Make a new BookCopy, initially in good condition.
     * @param book the Book of which this is a copy
     */
    public BookCopy(Book book) {
        //throw new RuntimeException("not implemented yet");
        this.title = book.getTitle();
        this.authors = book.getAuthors();
        this.year = book.getYear();
        this.parentBook = book;
        this.condition = Condition.GOOD;
        this.checkRep();
    }
    
    // assert the rep invariant
    private void checkRep() {
        //throw new RuntimeException("not implemented yet");
        assert this.parentBook != null;
        assert condition == Condition.GOOD || condition == Condition.DAMAGED; 
    }
    
    /**
     * @return the Book of which this is a copy
     */
    public Book getBook() {
        //throw new RuntimeException("not implemented yet");
        return this.parentBook;
    }
    
    /**
     * @return the condition of this book copy
     */
    public Condition getCondition() {
        //throw new RuntimeException("not implemented yet");
        return this.condition;
    }

    /**
     * Set the condition of a book copy.  This typically happens when a book copy is returned and a librarian inspects it.
     * @param condition the latest condition of the book copy
     */
    public void setCondition(Condition condition) {
        //throw new RuntimeException("not implemented yet");
        this.condition = condition;
        this.checkRep();
    }
    
    /**
     * @return human-readable representation of this book that includes book.toString()
     *    and the words "good" or "damaged" depending on its condition
     */
    public String toString() {
        //throw new RuntimeException("not implemented yet");
        String book = this.parentBook.toString();
        String condition = this.condition == Condition.GOOD ? "good" : "damaged";
        book += "\n Condition: " + condition ;
        return book;
    }

    // uncomment the following methods if you need to implement equals and hashCode,
    // or delete them if you don't
     @Override
     public boolean equals(Object that) {
         //throw new RuntimeException("not implemented yet");
         if(!(that instanceof BookCopy))
         {
             return false;
         }
         
         BookCopy thatBook = (BookCopy)that;
         System.out.println("Equals hitting");
         return thatBook == this;
     }
     
     @Override
     public int hashCode() {
         //throw new RuntimeException("not implemented yet");
         int result = 2;
         int code = 0;
         code += this.parentBook.hashCode();
         
         result = 37 * result + code;
         return result;
         
     }
     
     public static void main(String [] args) {
         Book book = new Book("Test Title", Arrays.asList("Furrukh", "Jamal"), 1985);
         BookCopy copy1 = new BookCopy(book);
         BookCopy copy2 = new BookCopy(book);
         BookCopy copy3 = new BookCopy(book);
         
         System.out.println(copy1.equals(copy2));
         System.out.println(copy1.getBook().equals(copy2.getBook()));
         System.out.println(copy1 == copy2);
         
         Set<BookCopy> test = new HashSet<>();
         test.add(copy2);
         test.add(copy1);
         System.out.println(test);
         
         Map<Book, Set<BookCopy>> test2 = new HashMap<>();
         test2.put(book, test);
         System.out.println("MAP");
         System.out.println(test2);
         
         test2.get(book).add(copy3);
         
         System.out.println("NEW MAP");
         System.out.println(test2);
         
         System.out.println("_______________");
         System.out.println("After Removeing a book from set");
         System.out.println("_______________");
         
         test.remove(copy1);
         System.out.println(test);
         
     }

}


Solution 1:[1]

In order to correctly use a Set, the hashcode must be unique(-ish) + the equals should fail for hash collisions.

The hash code for both bookCopy instances will be equal, since they are exact copies plus the only property used is book, so even if there would be some other difference it would still give the same hashvalue.

I believe you either need to add some ID the a BookCopy and take that into account in hash and equals function or use a List instead of a Set.

You should also review your equals method.

See java equals and hashcode

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Glenner003