简体   繁体   中英

How to remove ONE of the duplicates in an ArrayList in Java

I have an ArrayList filled with a number of the same books. Now I need to be able to implement a method lendBook (String bookName, String lenderName) that removes the last one of this list. If I use books.get(bookName).remove(book) nothing happens. If I use books.get(bookName).remove(books.get(bookName).get(books.get(bookName).size()-1)) it removes the whole ArrayList. Thanks for your help in advance.

public class Library extends HashMap {

    public Map<String, ArrayList<Book>> books;
    protected Map<String, Lender> lenders;

    public Library() {
        this.books = new HashMap<String, ArrayList<Book>>();
        this.lenders = new HashMap<String, Lender>();
    }


    public void addBook (String bookName){
        String key = bookName;
        if (books.containsKey(key)){
           books.get(key).add(new Book(bookName));
        } else {
            books.put(bookName, new ArrayList<Book>());
            books.get(key).add(new Book(bookName));
        }
    }

    public void addLender (String lenderName) throws java.lang.IllegalStateException {
        String key = lenderName;
        if (lenders.containsKey(key)){
            throw new java.lang.IllegalStateException(lenderName);
        } else {
            lenders.put(lenderName, new Lender(lenderName));
        }
    }
}

One solution might be to check for the last index in the ArrayList. You can see the size of the ArrayList with ArrayList.size(). So for instance

int size = ArrayList.size();

Then remove the last index.

The data structure you are using to store the books is a hashmap of String and an arraylist. Assuming the string to be book Name and arrayList containing of multiple book objects of the same book from which you want to delete last book

ArrayLiat<Book> booksForName = books.get(bookName);

Now to remove the last element of this list you just need to do

booksForName.removes(books.size()-1);

This can simply be done in a single line but the code will. be messy and not so easy to understand if you try reading after a few weeks:P

First, as commented , you should not be extending HashMap . Plus, I do not see how you were using Library as a Map .

Your use of the word "duplicates" in the title is misleading, as we are not dealing with duplicates of identical data. Instead, apparently you want to track multiple copies of a book.

You need to think through the problem domain to arrive at a proper data structure. A book is a concept (and a piece of intellectual property), with a title, an author(s), an identifier (ISBN etc.), a classification (such a Dewey Decimal number), and so on. Individual physical copies of a book are something else. Attributes of a copy include the identifier stamped on each by a librarian, and its condition as noted by a librarian. In our data model we would track the BookCopy objects as children related to a parent Book object.

Your desire that the lending method "removes the last one of this list" must mean that you want to lend our books per a LIFO policy: Last one in is first one out. Rather than use ArrayList as your collection, use an implementation of the interface meant for this purpose: Deque . ArrayDeque might be a good implementation for your purpose.

Actually, Deque is a bit confusing, as it can be used in different ways. For LIFO (Last-in, First-out) behavior, call:

As for adding items to your maps, your books and lenders maps are what is known as a multimap . The "multi" refers to multiple values per key rather than a single value per key.

Java now has built-in support for multimaps via the computeIfAbsent feature added to Java 8.

Take for example this BookCopy class. We use enum Condition for a librarian to record the physical condition of each book copy. We use a UUID value as an identifier to be stamped on each book by the librarian.

package work.basil.example;

import java.util.Objects;
import java.util.UUID;

public class BookCopy
{
    public enum Condition
    { PRISTINE, GOOD, SERVICABLE, DELAPIDATED }

    ;

    // Member fields
    private UUID uuid;
    private Condition condition;

    // Constructor
    public BookCopy ( UUID uuid , Condition condition )
    {
        this.uuid = Objects.requireNonNull( uuid );
        this.condition = Objects.requireNonNull( condition );
    }

    // Accessors

    public UUID getUuid ( ) { return this.uuid; }

    public Condition getCondition ( ) { return this.condition; }

    public void setCondition ( Condition condition ) { this.condition = condition; }

    // Object

    @Override
    public boolean equals ( Object o )
    {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        BookCopy bookCopy = ( BookCopy ) o;
        return uuid.equals( bookCopy.uuid );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash( uuid );
    }

    @Override
    public String toString ( )
    {
        return "BookCopy{ " +
                "uuid=" + uuid +
                " | condition=" + condition +
                " }";
    }
}

Apparently you want to track which copies are available at the library for lending. For each book, we need a Deque containing copies available for loan. If we track each book by its title as a String , we would have a Map where the key is String (title), and the value is a Deque of BookCopy objects.

Map < String , Deque < BookCopy > > copiesOnHand = new HashMap <>();

As mentioned above, you really should have a Book class in addition to a BookCopy class. If so, would use a Book object rather than merely the title String .

Map < Book , Deque < BookCopy > > copiesOnHand = new HashMap <>();

But for the sake of simplicity in this short demo, we will stick to using a String title to represent the book.

First, we add book copies to that map. The computeIfAbsent line first checks to see if your specified key has an entry in the map with a non-null value. If not, the value (an empty ArrayDeque ) is instantiated and added.

// Two copies of this book to lend.
String title = "Java Concurrency in Practice";

BookCopy bookCopy1 = new BookCopy( UUID.fromString( "61f036b5-da62-41fe-a765-f76bc31eebcb" ) , BookCopy.Condition.PRISTINE );
copiesOnHand.computeIfAbsent(
        title ,                                // key.
        ( String key ) -> new ArrayDeque <>()
)
        .addFirst( bookCopy1 );                      // value.

BookCopy bookCopy2 = new BookCopy( UUID.fromString( "731010a2-623a-4200-963a-9ce18f4fe988" ) , BookCopy.Condition.SERVICABLE );
copiesOnHand.computeIfAbsent(
        title ,                                // key.
        ( String key ) -> new ArrayDeque <>()
)
        .addFirst( bookCopy2 );                      // value.

Lending means we remove and return the last item in the deque.

// Debug: See our current inventory.
System.out.println( "copiesOnHand = " + copiesOnHand );

// Lend a book.
BookCopy copyToLend = copiesOnHand.computeIfAbsent(
        title ,                                // key.
        ( String key ) -> new ArrayDeque <>()
)
        .removeFirst();
if ( Objects.nonNull( copyToLend ) )
{
    System.out.println( "Lending book copy ID: " + copyToLend.getUuid() );
} else
{
    System.out.println( "Sorry, no copies of that book available to lend. " );
}

// Debug: Review our inventory after lending.
System.out.println( "copiesOnHand = " + copiesOnHand );

When run.

copiesOnHand = {Java Concurrency in Practice=[BookCopy{ uuid=731010a2-623a-4200-963a-9ce18f4fe988 | condition=SERVICABLE }, BookCopy{ uuid=61f036b5-da62-41fe-a765-f76bc31eebcb | condition=PRISTINE }]}

Lending book copy ID: 731010a2-623a-4200-963a-9ce18f4fe988

copiesOnHand = {Java Concurrency in Practice=[BookCopy{ uuid=61f036b5-da62-41fe-a765-f76bc31eebcb | condition=PRISTINE }]}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM