Find repeated elements in array list - java

The following code is suppose to take a string for author and title to check if that exact book is in the array list if it is then it would return the number of copies in the array. So far it's only checking if the book is in the array list, but I was wondering if there is any IPA that I can use to find repeated elements in an array list
public class Book{
private title;
private author;
}
public class Library {
private ArrayList<Book>libraryBooks;
public int checkNumCopies(String title,String author){
int numBookCopies = 0;
for(Book b:libraryBooks){
if((b.equals(title))&& (b.equals(author))){
return "Book is in the library";
}
else
return "Book is not in the library";
}
}

With proper implementation of equals and hashcode implementation in Book
class Book {
private String author;
private String title;
//Getters and Setters
//hashCode and equals impl
//toString impl
}
this can be achieved using streams
List<Book> books = Arrays.asList(new Book("book-1", "title-1"), new Book("book-2", "title-2"), new Book("book-3", "title-3"),
new Book("book-1", "title-1"), new Book("book-2", "title-2"));
Map<Book, Long> bookCount = books.stream().collect(Collectors.groupingBy(b -> b, Collectors.counting()));
System.out.println(bookCount);
output
{Book [author=book-3, title=title-3]=1, Book [author=book-2, title=title-2]=2, Book [author=book-1, title=title-1]=2}

Your Book fields are missing types (I assume you wanted Strings), and you have no accessor (or getters) for those fields. Also, I would probably override equals and use that. Something like,
public class Book {
public Book(String title, String author) {
this.title = title;
this.author = author;
}
private String title;
private String author;
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Book) {
Book b = (Book) obj;
return b.title.equals(title) && b.author.equals(author);
}
return false;
}
}
And then to use it, I would do something like
private List<Book> libraryBooks = new ArrayList<>();
public int checkNumCopies(String title, String author) {
Book toFind = new Book(title, author);
int numBookCopies = 0;
for (Book b : libraryBooks) {
if (b.equals(toFind)) {
numBookCopies++;
}
}
return numBookCopies;
}
or in Java 8+, like
Book toFind = new Book(title, author);
return (int) libraryBooks.stream().filter(book -> book.equals(toFind)).count();

Or you could create an equals method on Book and then use a HashSet. Something like
public class Book{
private title;
private author;
//implement hashcode
public boolean equals(Object o) {
if (!(o instanceof Book)){ return false}
Book that = Book.class.cast(o);
// you may want to do a null check here
return this.title.equals(that.title) && this.author.equals(that.author);
}
}
public class Library {
private ArrayList<Book>libraryBooks;
public int checkNumCopies(String title,String author){
return libraryBooks.size() - new HashSet(libraryBooks).size()
}
}
Im not sure entirely on what you need (sorry cant quite understand the question) but hopefully this at least gives you some ideas.
note: I havent compiled this so please excuse any compilation errors.

Here is the solution using java streams API
public class Library {
private ArrayList<Book> libraryBooks;
public long checkNumCopies(String title, String author) {
return libraryBooks
.stream()
.filter(book -> book.getAuthor().equals(author) && book.getTitle().equals(title))
.count();
}
}
I presupposed that there are getter methods in Book class.

If you want to find the number of copies of a book, that would be:
public int checkNumCopies(String title, String author) {
int numBookCopies = 0;
for (Book b : libraryBooks) {
if ((b.title.equals(title)) && (b.author.equals(author))) {
numBookCopies++;
}
}
return numBookCopies;
}
Otherwise, instead of sending title and author individually you can send a book object. That'd be better way :
public int checkNumCopies(Book book) {
int numBookCopies = 0;
for (Book b : libraryBooks) {
if ((b.title.equals(book.title)) && (b.author.equals(book.author))) {
numBookCopies++;
}
}
return numBookCopies;
}
Since you have made the fields private, you need to write getter methods for getting the values from the fields.

Related

How to use Java subclasses

I have 3 classes, Book, ChildrensBook and Library. ChildrensBook extends the Book class.
ChildrensBook contains the additional variable recommendedAge.
Library contains an array that can include both Book and ChildrensBook objects.
In the class Library I have to create the method int forChildren(int n) that returns how many childrensBook of age less or equal to n are there in array Library.
the problem is that in the library array there are both Books and ChildrensBooks, so I can't access the recommendedAge variable, because it is only inside a children's book. how can I do?
public class Library {
private ArrayList <Book> collection;
public Library(ArrayList <Book> c){
collection=c;
}
public int forChildren(int n) {
int count=0;
for(int i=0;i<collection.size();i++) {
if((collection.get(i).getRecommendedAge)<=n) {
count++;
}
}
return count;
}
public class Book {
private String title;
private String author;
public Book(String title, String author){
this.title=title;
this.author=author;
}
}
public class ChildrensBook extends Book {
private int recommendedAge;
public ChildrensBook(String title,String author,int recommendedAge){
super(title,author);
this.recommendedAge=recommendedAge;
}
public int getRecommendedAge() {
return recommendedAge;
}
}
Basically I see three possible approaches:
Pull the knowledge about recommended ages up to the Book class so that every book has a recommended age although you then need to decide which recommended age a non children's book has.
Decide on iterating over the books if a Book is a children's book or not - this can be achieved using Java's instanceof operator although this is not particularly object oriented.
Add a method similar to isValidForAge for books deciding if a book is valid for a certain given ages or not which every children's book answers based on the recommended age and every non children's book needs to answer based on other criteria.
This is improper class design.
You mixed a concern of books classification (children, adult, science-fiction, humor) which is more like a category or "tag" and the particular behavior (which is suitability for the audience).
You may decide to make a Book class abstract and inherit a concrete AdultBook class from it.
As already mentioned in other answer, you could have an adult book which is still suitable for children. Let's say a biography or an encyclopedia.
Which means, you may want to exercise getRecommendedAge() on every book in your collection.
You can use instanceof operator to check the type of the object. Try the below code:
public class Library {
private ArrayList<Book> collection;
public Library(ArrayList<Book> c) {
collection = c;
}
public int forChildren(int n) {
int count = 0;
for (int i = 0; i < collection.size(); i++) {
if (collection.get(i) instanceof ChildrensBook) {
if (((ChildrensBook) collection.get(i)).getRecommendedAge() <= n) {
count++;
}
}
}
return count;
}
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
}
public class ChildrensBook extends Book {
private int recommendedAge;
public ChildrensBook(String title, String author, int recommendedAge) {
super(title, author);
this.recommendedAge = recommendedAge;
}
public int getRecommendedAge() {
return recommendedAge;
}
}

I do not understand how we can use method compareTo inside a method compareTo

I am doing this project where I would get some number of books and I should sort it ,following this logic:
If a book has more pages than the other, then the book with the more pages goes first.
If both books have the same number of pages, then sort by the title alphabetically.
If both books have the same number of pages and the same title, then sort by the author alphabetically.
I tried to solve but did not manage to, so I looked up in the solution:
public class Book implements Comparable<Book>{
public int compareTo(Book specifiedBook) {
// First check if they have different page counts
if(this.numberOfPages != specifiedBook.numberOfPages){
// this will return a negative value if this < specified but will return a positive value if this > specified
return this.numberOfPages - specifiedBook.numberOfPages;
}
// If page counts are identical then check if the titles are different
if(!this.title.equals(specifiedBook.title){
return this.title.compareTo(specifiedBook.title);
}
// If page titles are also identical then return the comparison of the authors
return this.author.compareTo(specifiedBook.author);
}
}
What I do not understand here is this part 'return this.title.compareTo(specifiedBook.title);'. How can we use method compareTo inside a method compareTo , wouldn't it that be a recursion, I Know that as we implemented the interface comperable we must override the method compareTo, but how can we use that compareTo method inside the Overriden compareTo method? And what should it do, in this case and in general?
What confuses me, is how can we use the method that is just declared in interface, it is not inherited from the parent class. It is just declared in the comparable interface so when we implement the interface we must override it
Assuming that title and author are strings, you're calling String.compareTo (which is already implemented), from inside Book.compareTo. It's not recursion. It's a method in a different class.
The most easiest and better way to use java.util.Comparator for comparing fields. I would highly recommend to override equals and hashCode method since you are going to put the books in some container which might be a Sorted Set or something like that. Anyway, here is the example code,
import org.junit.Test;
import java.util.Comparator;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.TreeSet;
public class BookTest {
#Test
public void compareBooks() {
Book b1 = new Book(100, "A book", "Zoro");
Book b2 = new Book(10, "Small book", "ABC");
TreeSet<Book> books = new TreeSet<>();
books.add(b1);
books.add(b2);
System.out.println(books);
}
private class Book implements Comparable<Book> {
private final int numberOfPages;
private final String title;
private final String author;
private Book(int numberOfPages, String title, String author) {
this.numberOfPages = numberOfPages;
this.title = title;
this.author = author;
}
public int getNumberOfPages() {
return numberOfPages;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
#Override
public int compareTo(Book that) {
return Comparator.nullsFirst(
Comparator.comparing(Book::getNumberOfPages)
.thenComparing(Book::getTitle)
.thenComparing(Book::getAuthor))
.compare(this, that);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return numberOfPages == book.numberOfPages
&& title.equals(book.title)
&& author.equals(book.author);
}
#Override
public int hashCode() {
return Objects.hash(numberOfPages, title, author);
}
#Override
public String toString() {
return new StringJoiner(", ", Book.class.getSimpleName() + "[", "]")
.add("numberOfPages=" + numberOfPages)
.add("title='" + title + "'")
.add("author='" + author + "'")
.toString();
}
}
}

Constructing an object from a string

Is it possible to construct an object given a string, toString() method, and the Class itself.
For example we have class Book.
class Book
{
// ...
String getTitle()
{
return title;
}
String getPubYear()
{
return pubYear;
}
void setTitle(String _title)
{
title = _title;
}
void setPubYear(String _pubYear)
{
pubYear = _pubYear;
}
public String toString(){
return title+" "+pubYear;
}
}
If we have the String:
"ExampleTitle 2017"
How can we create an instance of the class Book, with which has attribute:
title=ExampleTitle
pubyear=2017
We can do the following:
Book book = new Book();
String exampleString = "ExampleTitle 2017";
String[] parts = exampleString.split();
book.setTitle(parts[0]);
book.setPubYear(parts[1]);
But this is long winded. Is there a more automatic way to do this?
You can add a new constructor:
public Book(String s) {
String[] parts = s.split(" ");
if (parts.length() == 1) {
this.title = s;
} else {
this.title=parts[0];
this.pubYear(Integer.parseInt(parts[1]));
}
}
You should add NumberFormatException handling on your own but I recommend my post about it.
The above constructor will take a String, split it by a space and perform what you have done. You can use it like:
Book book = new Book("Example_title 2001");
but it's not the best approach. You should rather use standard patterns and first extract the values you want to set to your Book and then pass them to the constructor.
The good way of doing what you want will be to make another constructor:
public class Book {
public Book(String s, int y) {
this.title = s;
this.year = y;
}
}
Please change year field to int or long. Year shouldn't be a String as long as you're not using Roman numbers.

(Library) checked out status returning all "true"

Instead of using boolean to see if a book was checked out I thought using a string to represent it was what I wanted but when I call the method to checkout a book the string applies to the entire array list of book objects. How can i change the status of just that book ?
import java.util.*;
public class Library
{
String owner;
int numBooks;
boolean isCheckedOut;
String status = "false";
ArrayList<Book> bookList = new ArrayList<Book>();
public void addBook(Book b)
{
bookList.add(b);
}
public String checkout(Book c) {
status = "true";
return status;
}
public Library(String o)
{
owner = o;
}
public String toString()
{
String s = "Owner: \t" + owner + "\nSize: \t" +
bookList.size()+"\nBooks: \t";
for( int i = 0;i < bookList.size();i++)
{
String title = bookList.get(i).getTitle();
//System.out.println(title);
s+="\n\t\t\t" +title +" Checked out: " + status; //bookList.get(i).getStatus();
}
return s;
}
public static void main(String [] args)
{
Library l = new Library("Jeremiah");
System.out.println(l);
}
}
i
public class Mainn
{
public static void main(String [] args)
{
Book book1 = new Book("Aambi","Aisney","Aalt","1942",1453);
Book book2 = new Book("Bambi","Bisney","Balt","1942",1453);
Book book3 = new Book("Cambi","Cisney","Calt","1942",1453);
Book book4 = new Book("Dambi","Disney","Dalt","1942",1453);
Book book5 = new Book("Eambi","Eisney","Ealt","1942",1453);
Book book6 = new Book("Fambi","Fisney","Falt","1942",1453);
Book book7 = new Book("Gambi","Gisney","Galt","1942",1453);
Book book8 = new Book("Hambi","Hisney","Halt","1942",1453);
Book book9 = new Book("Iambi","Iisney","Ialt","1942",1453);
Book book10 = new Book("Jambi","Jisney","Jalt","1942",1453);
Book book11 = new Book("Kambi","Kisney","Kalt","1942",1453);
Library l = new Library("Jeremiah");
// l.addBook(new Book("Xambi","Xisney","Xalt","1579",765));
l.addBook(book1);
l.addBook(book2);
l.addBook(book3);
l.addBook(book4);
l.addBook(book5);
l.addBook(book6);
l.addBook(book7);
l.addBook(book8);
l.addBook(book9);
l.addBook(book10);
l.addBook(book11);
// this changes all of the values to true instead of just this one
l.checkout(book1);
l.checkout(book11);
l.addBook(book1);
System.out.println(l);
}
}
You are storing the status against the Library, and not against each Book. To check out a single book, you'll need to put the status field into the book, and then set the book's status:
public class Book
{
public String status;
// etc...
}
public String checkout(Book c) {
c.status = "true";
return c.status;
}
Storing a boolean value as a string isn't usually the best idea - you'd normally be better off using an actual Boolean field for this.
A cleaner (more object oriented) implementation would look something like:
public class Book {
private boolean checkedOut = false;
public void checkout() {
checkedOut = true;
}
public void checkin() {
checkedOut = false;
}
public boolean isCheckedOut() {
return checkedOut;
}
}
This keeps the logic for managing a book with the book itself, and would allow you to easily add validation, such as preventing checkout if the field is already set to true.
So a big problem I'm seeing here is that you don't have the "Status" attribute on each book separately, you just have the one status String for the entire library.
It's not storing a status for each book.
What you could do is add an attribute called status to the Book class, and in the method where you check it out, you would set that to false. When printing them, you would print the status of each book by accessing this variable within each book object.
I think, the following piece of code help you:
public class Book {
private boolean status;
public boolean getStatus() {
return this.status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
public class Library {
public boolean checkout(Book c) {
c.setStatus(true);
return c.getStatus();
}
}
And I advice to you use a boolean primitive type instead a String object.

Creating an ArrayList and adding items

Im learning Java and having a problem with ArrayList.
Firstly I have a class called Item, with which I create various item objects.
Then I have a class Catalogue which is an array list and should hold a list of the item objects I create.
At the moment I can manually add the items to the catalogue by invoking an addItem method on the Catalogue object and manually entering the name of the item object I want to add (item1 item2 item3 etc)
But I wanted to know if there is a way to add the items to the ArrayList automatically each time I create an item object?
I should mention, my list needs to hold an infinite amount of items, so I have not specified a size in my code.
Any help would be greatly appreciated :)
Thanks
import java.util.ArrayList;
public class Catalogue
{
private ArrayList<Item> catalogue;
public Catalogue ()
{
catalogue = new ArrayList<Item>();
}
public void addAnItem(Item item)
{
catalogue.add(item);
}
}
Use the Catalogue as an Item factory:
public class Catalogue
{
...
public Item createItem()
{
Item item = new Item();
catalogue.add(item);
return item;
}
...
}
Another approach: Make Catalogue singleton and let the items add themselves.
One way you could do this, is if you passed the Catalogue into the constructor of the Item class, and once the item is set up, add the item to the catalogue at that point.
It may look something like this
public Item(Catalogue catalogue) {
// set up item here
// finally add item to the catalogue
catalogue.addAnItem(this);
}
I have put some comments at Matten and Codemwnci's answers, and here is an explanation of them.
Codemwnci suggests that you should not be able to construct an Item without setting its catalogue.
public class Item {
public Item(Catalog catalog) {
// set up item here
// finally add item to the catalog
catalog.addAnItem(this);
}
}
This explicit constructor removes the implicit default (no-arg) constructor, and you cannot construct an Item without it having a valid, non-null catalog.
If you have various types of items, with (slightly) different behaviour, you might be better served with Matten's answer (although slightly changed here).
As an example I'm using a Book (which is your Item). My Book has a title, author, textAtTheBack, and weight.
interface Book {
String getTitle();
String getAuthor();
String getTextAtTheBack();
Long getWeight(); // in grams, can be very heavy!
}
public class Catalog {
private ArrayList<Book> catalogue;
public Book createPaperback(final String title, final String author,
final String tatb, final Long weight) {
Book b = new Book() {
String getTitle() { return title; }
String getAuthor() {return author; }
String getTextAtTheBack() {return tatb;}
Long getWeight() {return weight;}
}
catalogue.add(b);
return b;
}
public Book createEBook(final String title, final String author,
final String tatb) {
Book b = new Book() {
String getTitle() { return title; }
String getAuthor() {return author; }
String getTextAtTheBack() {return tatb;}
Long getWeight() {return 0;} // Yep - no weight!
}
catalogue.add(b);
return b;
}
}
Alternatively, you could have different catalogues:
public abstract class Catalogue {
private final List<Book> books = new ArrayList<Book>;
public abstract Book (final String title, final String author,
final String tatb, final Long weight);
/** Find the book with the given title (not null) in the current catalogue.
* #return the book, or null if not found.
*/
public void findBook(String title) {
for (Book b : books) {
if (b.getTitle().equalsIgnoreCase(title)) {
return b;
}
}
return null;
}
protected void addBookToCatalogue(Book b) {
books.add(b);
}
}
public class EbookCatalogue extends Catalogue {
public Book (final String title, final String author,
final String tatb, final Long weight) {
Book b = new Book() {
String getTitle() { return title; }
String getAuthor() {return author; }
String getTextAtTheBack() {return tatb;}
Long getWeight() {return 0;} // ignore weight
}
addBookToCatalogue(b);
return b;
}
}
In the rest of the program you can have multiple catalogues, each with a slightly different type of Book, but the program need not know that.
I think in this case the simple Constructor of codemwnci is best, but there alternative solutions if your situation warrants a more flexible solution.

Categories

Resources