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.
Related
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 have two classes in java: Movie and Book (it's a simplified example):
Book can have author:
public class Book {
public String author;
public Book(String a) {
this.author = a;
}
public String getAuthor(){
return author;
}
}
And Movie can have title:
public class Movie {
public String title;
public Movie(String t) {
this.title = t;
}
public String getAuthor(){
return title;
}
}
I'm trying to put all objects in a list like this:
ArrayList myList = new ArrayList();
Book book = new Book("William");
Movie movie = new Movie("Titanic");
myList.add(book);
myList.add(movie);
And afterwards I want to count how many books written by John do I have (or any other specific titles). However I can't apply getAuthor() or getTitle() method since java doesn't know what type of object it is
int counter = 0;
for (int i =0;i<myList.size();i++){
if (myList.get(i).getAuthor().equals("John") ){
counter +=1;
}
I would be able to use if clause, check every time for an object type, and apply different methods for different objects, but this is not viable, since in real-life case I have 20+ classes and it would make code very long and maintainable.
Can someone suggest a solution for this? Thanks in advance!
create an interface
public interface HasAuthor {
String getAuthor();
}
implement this interface in both your classes and use this:
List<HasAuthor> list = new ArrayList<>();
list.add(new Book());
list.add(new Movie());
long count = list.stream().filter(smth -> "John".equals(smth.getAuthor())).count();
You cannot be using ArrayList myList = new ArrayList(); in 2017. The world has moved on from that archaic and error-prone style of programming. Generics were added to the Java programming language in 2004, and since then, any attempt to use a generic class without a generic type argument issues a warning. Which brings me to the next issue:
You cannot be ignoring warnings in 2017. Actually, there was never a good time to be ignoring warnings. Your IDE ought to be issuing warnings when you try to do ArrayList myList = new ArrayList(); heed them.
So, bottom line is, you should not be putting books and movies in the same collection. If you have a book class that has an author, and a movie class that has a director, (I will ignore your example of movies having a title and returning that as "author", because it is nonsensical,) then you can have either an interface or an abstract base class called, say, Item, with a String getAuthor() method, which is implemented (overridden) in both Book and Movie.
Then, your myList will be an ArrayList<Item>, and since Item has a getAuthor() method, you will be able to do myList.get( 0 ).getAuthor() and it will work without having to know whether it is a book or a movie.
First, myList.get(i).getAuthor() == "John" won't work since strings need to be compared via equals() (look up tutorials on why).
Second, you need to know the type of your objects and cast accordingly in order to call a method (you could do without the cast using reflection but please don't try that at home). Thus when iterating over your list you need to check:
for (Object o : myList ) {
if (o instanceof Book && ((Book)o).getAuthor().equals("John") ){
counter +=1;
}
}
However, if you want one list to contain all books and movies you'd better provide a common interface or superclass:
//Make it abstract to not allow instances of this class directly
abstract class PieceOfArt {
private String creator;
public String getCreator() {
return creator;
}
}
class Book extends PieceOfArt {
//Access the creator as the author
//note that I do this just for demonstration purposes, just using getCreator() would be perfectly fine
public String getAuthor() {
return getCreator();
}
}
class Movie extends PieceOfArt {
//Access the creator as the director
//note that I do this just for demonstration purposes, just using getCreator() would be perfectly fine
public String getDirector() {
return getCreator();
}
}
List<PieceOfArt> myList = ...;
for( PieceOfArt p : myList ) {
if( p.getCreator().equals("John") {
...
}
}
Putting objects of different, unrelated types, such as Book and Movie, which don't have a common superclass (besides java.lang.Object) is bad practice.
You could define a common abstract superclass for these types, and then create a List of that type. For example:
public abstract class Product {
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;
}
}
class Book extends Product {
}
class Movie extends Product {
}
Create a List<Product> and work with that:
List<Product> products = new ArrayList<>();
Book book = new Book();
book.setTitle("Cooking");
book.setAuthor("Bob the Cook");
products.add(book);
Movie movie = new Movie();
movie.setTitle("Romance at sea");
movie.setAuthor("John");
products.add(movie);
int count = 0;
for (Product product : products) {
if (product.getAuthor().equals("John")) {
count++;
}
}
NOTE: Do not make the getAuthor method actually return the title in case of a Movie, that would make your program really confusing.
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.
I'm making a small RPG. There is an Item class which is the parent of each item in the game. These items could be Potion (which is a class) or Bandage (which is a class).
The Item class looks like this:
public class Item
{
int qty;
String name;
Hero hero1;
public void passHero(Hero hero1)
{
this.hero1 = hero1;
}
public void use()
{
if(qty == 0)
{
System.out.println("You have no more of this item to use.");
}
else
{
qty--;
}
}
public void addInv(int value)
{
qty = qty + value;
}
}
A method for passing in the Hero class.
A method for using an item.
A method for adding to the inventory of the item.
This method activates these item classes:
public void initializeItemInventory()
{
items[0] = new Potion();
items[1] = new Bandage();
}
And this method would theoretically print all the items and their quantities:
public void useInventory()
{
for(int i = 0; i<items.length; i++)
{
System.out.println("Enter: " + i + " for " + items[i].name);
}
int response = input.nextInt();
items[response].use();
}
The Potion class, as an example, has an instance variable like:
String name = "Potion";
So my question. Why isn't the name variable from Potion being called correctly in the useInventory method. It returns null which tells me it's returning the parent class Item name, and not the name of the individual subclass variables.
public class Item
{
int qty;
String name;
...
The Item class already has name, and that's what you access from an Item-typed variable:
items[0].name
So if you have
public class Potion extends Item
{
String name = "Potion";
...
then the Potion class has two name fields:
Potion p = new Potion();
System.out.println(p.name);
System.out.println((Item) p).name);
As you say, you want polymorphism, but it only applies to methods. Therefore you need a getter:
public class Item
{
String name;
public String getName() { return name; }
...
In the Potion subclass you may have
public class Potion extends Item
{
public Potion() { this.name = "Potion"; }
...
and items[0].getName() will now work as expected.
Additional note
I'll add this to show a bit of the power of polymorphism.
If you happened to have the name property always the same for all the instances of the same class, you could easily refactor your getter-based solution by completely eliminating the need to store a name variable:
public class Item
{
public String getName() { return "Generic item"; }
...
public class Potion extends Item
{
#Override public String getName() { return "Potion"; }
...
Instead of declaring a new variable in your subclass like "String name = "Potion";"
Use your constructor to pass the value to your superclass, something like this:
// the Item supuerclass has one constructor
public Item(name) {
this.name = name;
}
// the Potion subclass has one constructor
public Potion() {
super("Potion");
}
I am attempting to create an inventory tracking system. I have a class (in Java) called "InventoryItem" with the properties of name and quantity.
This works fine for simple objects, but what if my inventory item contains other inventory items, for example, a server with RAM?
Should I be creating my own datatype, or is there a better way to do this (linked listed maybe)? should my class extend whatever that datatype is or should I not bother creating my own class?
My class so far:
public class InventoryItem {
private String name;
private int quantity;
private InventoryItem childInventoryItem;
// CONSTRUCTORS
public InventoryItem() {
}
public InventoryItem(int quantity, String name) {
this.quantity = quantity;
this.name = name;
}
//GETTERS
public String getName() {
return name;
}
public int getQuantity() {
return quantity;
}
//SETTERS
public void setName(String name) {
this.name = name;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
A tree is usually what is involved in any parent-child relationship. If you aren't doing anything complicated, you can simply maintain an internal list that is basically List<InventoryItem> which contains any child items.
So all you would add to your class is something like this:
public class InventoryItem {
...
private List<InventoryItem> composingItems = new ArrayList<>(); //if still using Java 6 this must be new ArrayList<InventoryItem>();
...
public void addComposingItem(InventoryItem composingItem) {
composingItems.add(composingItems);
}
public List<InventoryItem> getComposingItems() {
//Enforce immutability so no one can mess with the collection. However
//this doesn't guarantee immutability for the objects inside the list;
//you will have to enforce that yourself.
return Collections.umodifiableList(composingItems);
}
}
There are many ways you can do this. I think the easiest way would be to create an array list.
ArrayList<InventoryItem> childInventory = new ArrayList<InventoryItem>();
Then create a setter that adds inventory items to this array
public void addChildItem(InventoryItem child)
childInventory.add(child);
This way you would have a list of all of the child items within the item. You could also make a method to return a list of all of the child items in either an array or an ArrayList.
public ArrayList<InventoryItem> getChildItems()
return childInventory;