How to Sort Objects from ArrayList - java

Background information: I have a text file of books filled with their respected information (ex: title, publisher, pageCount). I have successfully created an inheritance hierarchy with all the correct implementations, get/setters, and toString methods. The inheritance hierarchy essentially looks like this (code will be provided further below):
Book
title
publisher
pageCount
FictionBook inherits Book
author
genre
NonFictionBook inherits Book
language
Dictionary inherits NonFictionBook
versionNumber
CookBook inherits NonFictionBook
topic
Novel inherits FictionBook
isPartOfASeries (i.e. Y or N)
GraphicNovel inherits FictionBook
illustrator
The text file looks like this:
My problem: I have been following this example: https://beginnersbook.com/2013/12/java-arraylist-of-object-sort-example-comparable-and-comparator/ but I do not fully understand how to use the compareTo method and further accurately sort the info into the correct classes. In my current code, my compareTo method seems to be printing the whole string and not accurately sorting it. I will provide all related code, output, and the parent class for the inheritance hierarchy class for better understanding.
My Question: How do I use the compareTo and collections.sort methods to accurately sort and print out my data. I have been stuck on this for a while so any guidance to me solving and learning this is appreciated!
Book Class (The Parent Class w/ Comparator and compare method):
public abstract class Book implements Comparable {
public String title;
public String publisher;
public int pageCount;
public Book(String title, String publisher, int pageCount) {
this.title = title;
this.publisher = publisher;
this.pageCount = pageCount;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public static Comparator<Book> bookTitleComp = new Comparator<Book>() {
public int compare(Book b1, Book b2) {
String bookTitle1 = b1.getTitle().toUpperCase();
String bookTitle2 = b2.getTitle().toUpperCase();
return bookTitle1.compareTo(bookTitle2);
}
};
// #Override
public String toString() {
return "Book " + "title: " + title + ", publisher: " + publisher + ", pageCount: " + pageCount;
}
}
Main Class (Here I am reading in my text file, sorting by the first number in the text field, so 1 = Dictionary, 2=Cookbook, 3=Novel, 4=GraphicNovel, and trying to print my compare method: last 4 lines)
ArrayList<Book> library = new ArrayList<Book>(); //Initialize ArrayList library
//Read text file in
FileReader fr = null;
try {
fr = new FileReader("library.txt"); //Reads in text file
} catch (FileNotFoundException ex) {
Logger.getLogger(GUICommandFunctions.class.getName()).log(Level.SEVERE, null, ex);
}
BufferedReader reader = new BufferedReader(fr);
try {
String line;
while((line=reader.readLine())!=null) {
System.out.println(line);
String[] splitLine = line.split(", ");
if("1".equals(splitLine[0])) {
library.add(new Dictionary(splitLine[1], splitLine[2], Integer.parseInt(splitLine[3]), splitLine[4], splitLine[5]));
} else if ("2".equals(splitLine[0])) {
library.add(new Cookbook(splitLine[1], splitLine[2], Integer.parseInt(splitLine[3]), splitLine[4], splitLine[5]));
} else if ("3".equals(splitLine[0])) {
library.add(new Novel(splitLine[1], splitLine[2], Integer.parseInt(splitLine[3]), splitLine[4], splitLine[5], splitLine[6]));
}else if ("4".equals(splitLine[0])) {
library.add(new GraphicNovel(splitLine[1], splitLine[2], Integer.parseInt(splitLine[3]), splitLine[4], splitLine[5], splitLine[6]));
}
}
} catch (IOException ex) {
Logger.getLogger(GUICommandFunctions.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Book title sorting: ");
Collections.sort(library, Book.bookTitleComp);
for(Book str: library) {
System.out.println(str);
}
Current Output:
Book title sorting:
Novel isPartOfSeries: N
Novel isPartOfSeries: N
Cookbook topic: French Cooking
GraphicNovel illustrator: Neil Gaiman
Cookbook topic: Asian Cooking
Novel isPartOfSeries: Y
Novel isPartOfSeries: Y
Cookbook topic: Keto Cooking
Novel isPartOfSeries: Y
Dictionary version number: 4
Dictionary version number: 2
GraphicNovel illustrator: Dave Gibbons
GraphicNovel illustrator: Pia Guerra
BUILD SUCCESSFUL (total time: 3 seconds)

follow the below example
import java.util.*;
public class CustomObject {
private String customProperty;
public CustomObject(String property) {
this.customProperty = property;
}
public String getCustomProperty() {
return this.customProperty;
}
public static void main(String[] args) {
ArrayList<Customobject> list = new ArrayList<>();
list.add(new CustomObject("Z"));
list.add(new CustomObject("A"));
list.add(new CustomObject("B"));
list.add(new CustomObject("X"));
list.add(new CustomObject("Aa"));
list.sort((o1, o2) -> o1.getCustomProperty().compareTo(o2.getCustomProperty()));
for (CustomObject obj : list) {
System.out.println(obj.getCustomProperty());
}
}
}

Related

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.

Find repeated elements in array list

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.

Loop for ArrayList printing out null

I'm new to Java so sorry for all the mistakes!
Im creating a Library program consisting of 4 classes: Library, Book, BookInterface & Patron.
In the Book class I have a method that prints out all the books in the library and their status' (in or out). Instead I keep getting something like this:
Great Gatsby: null
Withering Heights: null
Does it have something to do with the setStatus() method?
Every time a user adds a new book, it creates a new Book instance and then I do setStatus("IN"). So how come it is not saving and instead printing out null?
Thank you very much for the help!!
Book class:
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Book implements BookInterface
{
Scanner input = new Scanner(System.in);
static ArrayList <String> UserList = new ArrayList<String>();
static ArrayList <String> BookList = new ArrayList <String> (); //display just titles// use when checking out books
static ArrayList <String> OrigBookList = new ArrayList <String> (); //keep track of all titles ever entered
public String title;
public String author;
public String book;
public boolean checkIn;
private String status;
private String borrower;
public Book(String t, String a)
{
title = t;
author = a;
}
//constructor create new book
public Book(String newTitle)
{
title = newTitle;
}
public String toString()
{
return title + " " + author;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getAuthor()
{
return author;
}
public void setAuthor(String author)
{
this.author = author;
}
public String getStatus(String book)
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public void setBorrower(String borrower)
{
this.borrower = borrower;
}
public String getBorrower(String checkPatron)
{
return borrower;
}
public String getBook(String checkPatron)
{
return book;
}
public void setBook(String bookCheckOut)
{
this.book = bookCheckOut;
}
public void addBook()
{
Scanner input = new Scanner(System.in);
Scanner inputread = new Scanner(System.in);
System.out.print("Enter book title: ");
String title1 = inputread.nextLine();
Scanner input1 = new Scanner(System.in);
System.out.print("Enter book author: ");
String author1 = inputread.next();
Book fullBook = new Book(title1, author1); //create constructor w/ title & author
Book book1 = new Book(title1); //constructor w/ just title to be used to display all books
OrigBookList.add(title1);
book1.setStatus("IN");
System.out.println("-----------------------------------------------");
System.out.println("-----" + title1 + " is now in the library!-----");
System.out.println("-----------------------------------------------");
}
public void editBook()
{
Scanner inputread = new Scanner(System.in);
System.out.println("Enter original book title: ");
String origTitle = inputread.nextLine();
System.out.println("Enter edited book title: ");
String editedTitle = inputread.nextLine();
Collections.replaceAll(Book.UserList, origTitle, editedTitle);
System.out.println("------------------------------------------------------");
System.out.println(origTitle + " has been changed to " + editedTitle + "!");
System.out.println("------------------------------------------------------");
}
public void libraryInventory()
{
System.out.println("------------------ Library Inventory: ---------------");
for(int i =0; i<= OrigBookList.size()-1; i++)
{
//Book Title: checked in/out
System.out.println(OrigBookList.get(i) + ":" + getStatus(OrigBookList.get(i)));
}
System.out.println("-----------------------------------------------------");
}
}
getStatus(OrigBookList.get(i)) ignores the parameter you pass to it and just returns the status of the Book for which you called the libraryInventory method. Obviously, that Book instance doesn't have the status field initialized, but even if it did, it will give you the status of just one Book.
Instead of having a static list of book titles (static ArrayList <String>), perhaps you should maintain a list of the books themselves (static ArrayList <Book>), or even better, put that list in a separate class (you can call it Library).
Methods such as libraryInventory shouldn't be in the Book class (and if you insist on keeping them in the Book class, make them static, since they don't refer to a single Book instance).
Your whole program seems to be running inside an instance of the class Book. In it, you are making and discarding new instances of Book, called fullBook and book1, and for fullBook you set its status. When you call getStatus on the main Book in your program, it just returns its own status, which was never set to anything.
If you want to save a sequence of instances of Book, you need to put the instances somewhere, not just instantiate them and then add the title to a list.

(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.

Using an "add" method to add objects to an ArrayList from a separate class

I'm relatively new to Programming in general and I've been grafting over this piece of work for hours and haven't got any further. I've trawled through the depths of the internet and I cannot find an answer similar to what I am looking for. Here in the Spec:
A) Create two files FictionBook.java and Library.java.
Define a FictionBook class that represents a single book. The book will have a
title, an author, an availability field (1=available, 0=on loan). You should define
accessor methods to borrow and return the book and read the title and author
and an accessor method to return the availability.
Additionally, define a Library class that contains up to 200 books. The library
should model containing books with an ArrayList. The Library
class should contain a method to add a book to the library, delete a book from
the library and borrow and return books.
B) Create a Librarian.java file and modify the Library.java file.
Write code to sort the messages in the Library so that all the books are in
alphabetical order by Author’s name. Create a Librarian class with only a main
method, so you can simulate the processing of books in the library. Generate
10 new FictionBooks and add them to a Library using the addBook method.
Your Library should place the message in the correct place in the library
depending on the name of the author.
I think I have completed the first part, although I could be wrong. It is the second part which I am complete stuck on. Here are my three classes
public class FictionBook {
private String title, author;
private int availability;
public FictionBook(String title, String author, int availability){
super();
this.title = title;
this.author = author;
this.availability = availability;
}
public FictionBook() {
this.availability = 1;
}
public void borrowBook1() {
setAvailability(0);
}
public void returnBook1() {
setAvailability(1);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getAvailability() {
return availability;
}
public void setAvailability(int availability) {
if(availability != 1 || availability != 0) {
System.err.println("Enter 1 for available. Enter 2 for on loan.");
throw new IndexOutOfBoundsException();
} else {
this.availability = availability;
}
}
}
import java.util.ArrayList;
public class Library {
static ArrayList<FictionBook> BookList = new ArrayList<FictionBook>(200);
public static void addBook(FictionBook String){
BookList.add(String);
System.out.println("Book Successfully Added To Library Database.");
System.out.println(BookList);
}
public void deleteBook(){
BookList.remove(index);
}
public void borrowBook(){
BookList.get(index).FictionBook.borrowBook1();
}
public void returnBook(){
BookList.get(index).FictionBook.returnBook1();
}
}
public class Librarian {
public static void main(String args[]){
FictionBook newBook1 = new FictionBook("USB Man", "Bob", 1);
FictionBook newBook2 = new FictionBook("Bin Boys", "Chris", 1);
FictionBook newBook3 = new FictionBook("Dinosaur", "Joe", 1);
FictionBook newBook4 = new FictionBook("Pigasaurus", "Tom", 1);
FictionBook newBook5 = new FictionBook("Cat Attack", "Calvin", 1);
FictionBook newBook6 = new FictionBook("Shark Man", "Alfie", 1);
FictionBook newBook7 = new FictionBook("Burnt Face Man", "Colin", 1);
FictionBook newBook8 = new FictionBook("Egg Life", "Darwin", 1);
FictionBook newBook9 = new FictionBook("Pizza King", "Pringle", 1);
FictionBook newBook10 = new FictionBook("BillyBonka", "Randy", 1);
Library.addBook();
}
}
I am just wondering how I actually use the addBook(); method in my Library class to add the objects defined in the Librarian class to the ArrayList in my Library class? I've been messing around with the code a lot so there may be a lot of mistakes, apologies in advance. Any help would be super.
Thank you in advance for your time!
Look at where you defined the addBook method:
public static void addBook(FictionBook String)
this means that whenever you want to call addBook, you MUST include the name of the book (the name of the object of the FictionBook, not the literal title. And you must do this for each book since you're only doing them one at a time.
so try this
FictionBook newBook1 = new FictionBook("USB Man", "Bob", 1);
Library.addBook(newBook1);
FictionBook newBook2 = new FictionBook("Bin Boys", "Chris", 1);
Library.addBook(newBook2);
etc. etc. for each book you define
also, like Compass said, naming a local variable "String" is not a good idea at all. I would rename it something like book because it's technically not the name of the book, it's the name of the object.

Categories

Resources