Search for certain String inside array of objects - java

I couldn't find an answer so I have to post a new question - although it seems to be similiar to few another questions that were asked already here - but to the point:
I have got some list, for example:
private static List<Book> books = new ArrayList<>();
It contains objects of class Book, where I have got
private String title;
private String author;
and getters&setters.
Now I have got a new object Book (got from webform through parameters/servlet) and I want to check if title of new book is already on list books.
If it is already on that list, print some console output. If it is not, I want to add the whole new object to list. In order to do that, I tried to compare titles using regular foreach loops. Sometimes it worked out, sometimes not (sometimes my function cwere comparing two titles in a right way but sometimes not - I don't know why). I know there is a better way of doing this using Java 8. But I'm newbie when it comes to Java 8. Could somebody show me a good way of doing this?

I can't tell why you want to do it specifically with java-8 features, but it could be done like this:
boolean isPresent = books.stream()
.anyMatch(book -> book.getTitle().equals(inputBook.getTitle()));
if(isPresent){
... console print
} else {
add it to books
}
I think that this could be made a bit more generic btw, to have a method that would accept a Predicate.
private boolean exists(List<T> list, Predicate<T> predicate) {
return list.stream().anyMatch(predicate);
}
So that you could match anything you like later:
boolean exists = exists(books, p -> p.getTitle().equals(b.getTitle()));
Or :
Predicate<Book> first = p -> p.getTitle().equals(b.getTitle());
Predicate<Book> second = first.and(p -> p.getTitle().equals(b.getTitle()));
boolean exists = exists(books, second);

You can try with findAny and Optional
Optional<Book> found = books.stream().filter(p -> p.getTitle().equalsIgnoreCase(newBook.getTitle())).findAny();
if (found.isPresent()){
System.out.println(found.get());
}
else {
books.add(newBook);
}

If you wanted to be a little fancy and methodical you could create a 'Library' class that inherits or has-a ArrayList. What you need then is an equals function for book that uses String .equals() on the title. The contains method could then call this looping over the elements.

First you must create the getter's & setter's for the class Book. A demonstration of that would be like this:
class Book{
private String title;
private String 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;
}
}
Now you can do the following to surf through every objects' title and compare it with the newly entered objects' title.
Scanner in = new Scanner(System.in);
List<Book> books = new ArrayList<>();
Book b = new Book();
System.out.println("Enter the title:");
String t = in.nextLine();
b.setTitle(t); //optional
for (Book book:books
) {
if(book.getTitle().equals(t))
System.out.println("Title exists");
else
System.out.println("Title does not exist");
}

Related

How to search an array list for a string and remove it

I have to do a project for my beginners Java class that has to do with inheritance.
The MediaItem class encapsulates the data required to represent a MediaItem. Here is the code that was already given:
public class MediaItem {
protected String title;
protected String author;
protected String genre;
/* Subclasses may add specific parameters to their constructor's
* parameter lists.
*/
public MediaItem(String title, String author, String genre){
this.title = title;
this.author = author;
this.genre = genre;
}
// get method for the title
public String getTitle(){
return title;
}
// get method for the author
public String getAuthor(){
return author;
}
// get method for the genre
public String getGenre(){
return genre;
}
// Subclasses should override.
public String toString(){
return title+", "+author+", "+genre;
}
}
The MediaList class encapsulates a list of media items in a user's collection. The list is implemented as an ArrayList of type MediaList. Each type of media item is represented by an instance of the Book, Movie, Podcast, or Song class. These classes are subclasses of MediaItem. The list stores media items as references of type MediaItem.
Here is the code for adding and removing an item in MediaList:
public void addItem(MediaItem newItem){
itemList.add(newItem);
}
public boolean removeItem(String targetTitle, String targetAuthor){
boolean result = false;
for (MediaItem media : itemList) {
if(itemList.contains(targetTitle) && itemList.contains(targetAuthor)){
itemList.remove(media);
result = true;
} else {
result = false;
}
}
return result;
}
When this test runs with Junit, an error is thrown saying there expected size was <1> but it should be <0>
When I run it and answer the prompts, it says the media item is added but when I try to remove the media item it says "Could not find Black Panther in the library, nothing removed."
#Test
public void addOneRemoveOneItemUpdateSizeTest() {
MediaItem item = new Movie("Black Panther", "Coogler", "fantasy", 134, "Chadwick Boseman", "2018");
mediaList.addItem(item);
mediaList.removeItem("Black Panther", "Coogler");
int studentSize = mediaList.getNumItems();
assertEquals("Test 30: Add item, remove item size is 0.", 0, studentSize);
Movie class
public class Movie extends MediaItem {
public Movie(String title, String author, String genre,
int playTime, String leadActor, String releaseYear){
super(title, author, genre);
playTime = 0;
leadActor = "noLead";
releaseYear = "noRelease";
}
public int getPlayTime(){
return playTime;
}
public String getLeadActor(){
return leadActor;
}
public String getReleaseYear(){
return releaseYear;
}
#Override
public String toString(){
super.toString();
return "Movie: " + title + ", " + author + ", " + genre + ", " + playTime + ", " + leadActor + ", " + releaseYear;
}
}
Is my removeItem method wrong? I don't understand why the title can't be found. Can someone point me in the right direction?
I think the problem is this if statement:
if(itemList.contains(targetTitle) && itemList.contains(targetAuthor)){
It should be
if (media.getTitle().equals(targetTitle) && media.getAuthor().equals(targetAuthor))
In other words, you need to check whether each media item, rather than the list, has the right title and author.
There are two correction points for this code:-
The conditional statement is not correct
Deletion is being done incorrectly & it will lead to ConcurrentModification Exception
1. Conditional Statement Bug Explanation
The contains() method checks for the existence of passed object in the list. It is handy while dealing with wrapper classes or String class but whenever we have our custom data types such as classes and use case demands to remove the object on the basis of certain properties then this method may not be helpful.
Right now your code is checking for the String objects in your arguments 'targetTitle' and 'targetAuthor' and thus fail to remove the desired object from your list.
Your use case demands the removal of object from list on the basis of 'title' and 'author' which are the properties(instance variables) of MediaItem class and hence when you are traversing through all the objects in the list you should check the respective instance variables of the objects that you are traversing in the List.
Kindly use '==' instead of equals() method as you will have to do explicit null checks otherwise you can end up having NullPointer exception.
2. Incorrect Deletion from List
You are making the modification on the same object which you are traversing via for-each(Enhanced for) loop. You should make use of iterator object for this purpose. Please go through this link for further information.Using iterator for deleting elements in collection
I think you are removing item in the wrong way. You should remove item from list by using iterator or loop from the end of list.
Futhermore, the conditional statements is wrong too, you should use String.equals to check item's attributes that match inputs. In addition, you ought to validate input to prevent Null Pointer Exception.

How get elements from an ArrayList based by multiple conditions?

I'm creating an interface that manage the booking of Cinema tickets in different weeks/theatres.
I have a Film Class:
public class Film {
private String title;
private Double price;
private String ageRestriction;
private double rating;
private String genre;
private String location;
private String screenDay;
}
A FilmList class that create and store all the Films in an ArrayList:
public class FilmList {
private ArrayList <Film> filmArrayList;
public FilmList (){
this.filmArrayList = new ArrayList<>();
}
public void addFilm(Film films){
this.filmArrayList.add(films);
}
And this is the graphic unit interface. What I'm trying to do is to catch in the ArrayList just one element based on two condition:
The Week and the Theatre selected from the user and also add a way for check that there's just one element on the list from the chosen parameters. This is important because each film instance will be called and "setted" on the Label of the FXML file (and because I'm thinking to implement an interface for add films in the ArrayList).
Thank's everyone.
OK, so in your example it would be something like following:
//try to find any movie that suits two predicates, 1st - that it's price is greater than 30( this is random number, you can put a value from your textfield here ) and 2nd - that it's title contains Spiderman ( again, put a value from your textfield title search here for your need )
Optional<Film> movie = listOfMovies.stream().filter(i -> i.getPrice() > 30 && i.getTitle.contains("Spiderman")).findAny();
// if any movie has been found to suits provided criterias
if(movie.isPresent())
{
//print it on screen, note method get()
//again this is just for example here, in your code,
// you can do with this result whatever you like
// for example show all data about that movie on screen
System.out.println("---->" +movie.get());
}
else
{
// if not found do nothing
System.out.println("Nothing found...");
}
More about Optional can be found here.

Using a method on an object of an arraylist in Java

public class Book
{
private String isbn, author, area;
private int length;
public Book(String isbn, String author, String area, int length)
{
this.isbn=isbn;
this.author=author;
this.area=area;
this.length=length;
}
public boolean isLong()
{
if (length>500)
return true;
else
return false;
}
}
public class BookCollection
{
private ArrayList<Book> bookList = new ArrayList<Book>();
public BookCollection()throws IOException
{
Scanner fileScan;
String oneLine;
String isbn, author, area;
int length;
fileScan = new Scanner (new File("books.txt"));
while (fileScan.hasNext())
{
isbn=fileScan.next();
author=fileScan.next();
area=fileScan.next();
length=fileScan.nextInt();
bookList.add(new Book(isbn, author, area, length));
}
}
public class TestBookCollection
{
public static void main (String[] args)throws IOException
{
BookCollection books = new BookCollection();
System.out.println(books);
}
}
All right here is the relevant code. My project is to read a text file that has the information about these books and put them into an arraylist of book objects. My question is: how would I go about envoking the isLong() method found in class Book on an object in an arraylist? The point of the method is, if an object has >500 pages that it returns true. If not it will return false. I'm just kind of confused about the logic and I have never really worked with Arraylists before.
You can add another method to BookCollection:
public void printLongBooks() {
for (Book book : bookList) {
if (book.isLong()) {
well, book is long ...
} else {
obviously, it is short ...
}
The above uses the so called "for each" looping style in Java that you can use to loop every array/collection; instead of the "old school" for (int i=0; i<...) counting loop.
and within your main method, you simply invoke the new method:
books.printLongBooks()
And some generic hints:
A) isLong() can be reduced to a one-liner: return length > 500;
B) reading stuff from a file is not what you directly do in the constructor. Instead, you should create a dedicated method to that, which you then might call within the constructor
Rather than putting the logic to create book list inside a constructor, you should create a method named as getBookCollection() that returns an arraylist.
public List<Book> BookCollection()throws IOException
{
List<Book> bookList = new ArrayList<Book>();
//logic to create booklist
return booklist;
}
Once you have list of books, you can run an enhanced for loop and check for pages in each book.
for(Book book: bookList){
if(book.isLong()){
//logic
} else {
//logic
}
}
You can use a for loop to iterate through the elements of the ArrayList and call the method on each one of its objects.
for(Book book : bookList){
boolean isLong = book.isLong();
}
You have a List<Book>, where each index in the List contains a Book. Use the List.get(index) method to get the Object in the given List at index. For example: bookList.get(0) gets the Book at index 0. Once you have that Object, you can use it normally.
I assume that you have some way to get the bookList inside BookCollection, and the name of a Book?
public static void main(String[] args){
BookCollection books = new BookCollection(); // Make a new BookCollection
List<Book> booksInCollection = books.getBooksList(); // Get the Books inside BookCollection
// Go through each book in booksInCollection
for(int index = 0; index < booksInCollection.size(); index++){
// Get the Book Object at index
Book currentBook = booksInCollection.get(index);
if(currentBook.isLong()){
System.out.println(currentBook.getName() + " is long!");
}
}
}
Firstly (if you don't want to use streams etc.) you need to get object from ArrayList. You can do it by using ArrayList.get(int index) and then invoking the method or by using for each loop:
for(Book book : bookList) {
System.out.println(book.isLong());
}
The problem is that you cannot access private field (bookList) from main. There are several ways to make it work.
Create a public method inside BookCollection
Change field to public and then access it directly books.bookList
Make a getter to your bookList in BookCollection
Make BookCollection extends ArrayList
What you've done here is created a decorator (BookCollection) which wraps an ArrayList with additional functionality (in this case, a constructor which pre-fills it based on a file). This gives you two options for how to make use of a Book in the List:
Make the List accessible via a getter, and then work it out. Makes statements long but this is acceptable for small amounts of usage:
if(books.getBooks().get(index).isLong()) {
//The book at index is long
}
More commonly, you'll make methods in the decorator which offer common functionality, like so:
public boolean isLong(int index) {
return bookList.get(index).isLong();
}
Then you just call the decorator method from your business logic. You can even make more complex methods such as the one offered by GhostCat.

Java NullPointerException when referencing object array

I have the following program:
class Books
{
String title;
String author;
}
class BookTestDrive
{
public static void main(String [] args)
{
Books [] myBooks = new Books[3];
int x = 0;
myBooks[0].title = "The Grapes of Java";
myBooks[1].title = "The Java Gatsby";
myBooks[2].title = "The Java Cookbook";
myBooks[0].author = "bob";
myBooks[1].author = "sue";
myBooks[2].author = "ian";
while (x < 3)
{
System.out.print(myBooks[x].title);
System.out.print(" by ");
System.out.println(myBooks[x].author);
x = x + 1;
}
}
}
However, it gives me the following error when I execute it:
Exception in thread "main" java.lang.NullPointerException
at BookTestDrive.main(Books.java:14)
I am new to Java. The code looks legitimate from my C/C++ experience...How to resolve this problem?
The issue is that you have only created the array of books in the following lines -
Books [] myBooks = new Books[3];
You still need to initialize each element in the array to a book object before accessing them.
An example code would look like -
Books [] myBooks = new Books[3];
int x = 0;
myBooks[0] = new Books();
myBooks[0].title = "The Grapes of Java";
You need to do this for all elements in your array.
I second the answer from #AnandSKumar (it is the direct answer to the problem after all), but because it is a matter of beauty, I could not leave without making following few changes:
public class Play {
static public class Book {
final public String title;
final public String author;
public Book(String title,String author) {
this.title = title;
this.author = author;
}
#Override
public String toString() {
return "\""+title+"\" by "+author;
}
}
public static void main(String [] args)
{
Book [] books = new Book[] {
new Book("The Grapes of Java","bob"),
new Book("The Java Gatsby","sue"),
new Book("The Java Cookbook","ian"),
};
for (Book book:books) {
System.out.println(book);
}
}
}
You can initialize in-line the content of your array
If you represent 'a single book' then we should name the class representing it as Book and not Books to avoid confusion.
We can enhance the Book class with an improved toString(), and use that instead.
There is a enhanced for iterator to loop over your array.
Note that the third position book in the array, also ends with a comma, although there is no element following it. This could have been a mistake, but it this case it was a deliberate choice. Makes it easier to copy-paste into next elements without introducing errors, as the Java syntax allows for it.
Because once a book is created, title and author should not change anymore, it might be good to design the Book class to be 'Immutable'. For this reason a constructor was added, and the title and author fields set as final. You could also consider making them private, and provide getters.

Java search list of objects for variable

I have a List of Book
List<Book> books = new ArrayList<Book>();
public class Book {
String name;
List<Author> authors;
}
Book contains a List of Author which contains an ID, Name and Age
public class Author {
int ID;
String Name;
int Age;
}
I can iterate through the books List and return the Book where Name = x but I'm not sure how I can search Author and return where Name = y.
Add a method hasAuthor(String) to your Book which loops through the List<Author> list and compares the name. Like so:
boolean hasAuthor(String name) {
for (Author author : authors) {
if (author.name.equals(name)) {
return true;
}
}
return false;
}
To find books with a specific author, you loop over the List<Book> and invoke the hasAuthor(String) method.
Maybe this is off topic, but when solving such problems, having objects with nested lists, though logically simple, is a really bad idea. Its extremely hard to maintain, scale and work with.
If you use this structure throughout and come across a problem like:
Give me all the books written by this author.
You are toast, you will have to iterate over each and every book. Or start maintaining caches and what not. It gets worse when the nesting deepens.
Lets say, now you need to maintain each Author's list of hobbies too. Think of how would you implement something like : Give me all the books whose authors like skydiving!
I know this doesn't answer your question. Just my 2 cents.
Something primitive:
List<Author> authors;
...
Iterator<Author> ai = authors.iterator();
Author a;
while(ai.hasNext()){
a = ai.next();
if(a.name.equalsIgnoreCase(yourname)){
return a;
}
continue;
}
Edit: Too late :/

Categories