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.