Accessing variable of ArrayList inside class - java

The class Task is describing tasks for a business which includes the variables: date, description, total amount of hours the task will take to complete, and the owner of the task.
An ArrayList is created for all the tasks named tasks. The problem is that a task can have more than one owner, meaning that creating a variable called owner wont work, so what I've done is that I created another class called TaskOwner and implemented that class as an ArrayList named taskOwner inside the Task class.
Now to the problem: You are supposed to be able to list tasks by a specific owner: meaning that you need to compare owners to the name you enter on your keyboard.
The problem in this lies within these lines of code:
System.out.println("What name of owner do you want to list tasks for: ");
String nameOfOwner = keyboard.nextLine();
if (nameOfOwner.toLowerCase().equals(tasks.get(1).getTaskOwner().getName().toLowerCase())) {
System.out.println(tasks.get(1));
}
I can't seem to access the variable named name inside the class TaskOwner, even though I've created getters for everything that is needed, so does anyone know how I am supposed to be able to access this information?
The error message I get is:
The method getName() is undefined for the type ArrayList

getTaskOwner appears to return an object of type ArrayList and not TaskOwner (that's what the error message indicates). In other words, it returns a list of owners. To call the getName() method, you need to loop over this list, and call the method on each element corresponding to an instance of TaskOwner.
System.out.println("What name of owner do you want to list tasks for: ");
String nameOfOwner = keyboard.nextLine();
for(int i = 0; i < tasks.size(); i++) {
List<TaskOwner> owners = tasks.get(i).getTaskOwner();
for(TaskOwner owner : owners) {
if (nameOfOwner.toLowerCase().equals(owner.getName().toLowerCase())) {
System.out.println(tasks.get(i));
break;
}
}
}

This returns an ArrayList:
tasks.get(1).getTaskOwner();
So you will need to call array list methods on it such as contains(...)
if (tasks.get(1).getTaskOwner().contains(nameOfOwner.toLowerCase())) {

As pointed out in other answers, the problem is you are calling getName() on an ArrayList<> instead of an object inside the ArrayList. The correct way of doing this would be to loop over all the tasks and then for each task, loop over their owners. Here is a sample piece of code, assuming the owners name is stored in the variable nameOfOwner:
for(Task task: tasks) {
for(TaskOwner owner: tasks.getTaskOwner) {
if (nameOfOwner.toLowerCase().equals(owner.getName().toLowerCase())) {
System.out.println(task);
break;
}
}
}
If you have also overloaded the equals method in the class TaskOwner to do a string match for the owner's name, you could just use the Arraylist.contains() method. But then, you will need to create a TaskOwner object out of the user input.
If your intention is to do task and owner lookups, you should also consider using HashMap. This would give you a better performance than ArrayList<> for direct lookups.

Related

How can I replace object in java collection?

I am trying to replace element in collection with new modified version. Below is short code that aims to demonstrate what I'd like to achieve.
The whole idea is that I have one object that consists of collections of other objects. At some point in time I am expecting that this objects in collections (in my example phones) might require some modifications and I'd like to modify the code in one place only.
I know that in order to update the object's attributes I can use setters while iterating through the collection as demonstrated below. But maybe there is better, more general way to achieve that.
public class Customer {
private int id;
private Collection<Phone> phoneCollection;
public Customer() {
phoneCollection = new ArrayList<>();
}
//getters and setters
}
and Phone class
public class Phone {
private int id;
private String number;
private String name;
//getters and setters
}
and
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Collection<Phone> col = c.getPhoneCollection();
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
// This is working fine
// phone.setNumber(p.getNumber());
// phone.setName(p.getName());
// But I'd like to replace whole object if possible and this is not working, at least not that way
phone = p;
}
}
System.out.println(c);
}
}
Is this possible to achieve what I want?
I tried copy constructor idea and other methods I found searching the net but none of them was working like I would expect.
EDIT 1
After reading some comments I got an idea
I added the following method to my Phone class
public static void replace(Phone org, Phone dst){
org.setName(dst.getName());
org.setNumber(dst.getNumber());
}
and now my foreach part looks like that
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
Phone.replace(phone, p);
}
}
And it does the job.
Now if I change the Phone class attributes I only need to change that method. Do you think it is OK solving the issue that way?
You should not modify the collection while you're iterating through it; that's likely to earn you a ConcurrentModificationException. You can scan the collection for the first object that matches your search criterion. Then you can exit the loop, remove the old object, and add the new one.
Collection<Phone> col = c.getPhoneCollection();
Phone original = null;
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
original = phone;
break;
}
}
if (original != null) {
Phone replacement = new Phone(original);
replacement.setNumber(p.getNumber());
replacement.setName(p.getName());
col.remove(original);
col.add(replacement);
}
Alternatively, you could declare a more specific type of collection, such as a List, that would allow you to work with indexes, which would make the replacement step much more efficient.
If your phone IDs are unique to each phone, you should consider using a Map<Integer, Phone> that maps each phone ID to the corresponding phone. (Alternatively, you could use some sort of third-party sparse array structure that doesn't involve boxing each ID into an Integer.) Of course, if your IDs aren't unique, then you might want to modify the above to gather a secondary collection of all matching phones (and reconsider the logic of your existing code as well).
You can also use a Set (HashSet), this is only when you don't want to do the way Mike suggested.
Use the Phone as an item in the set. Don't forget to implement hashCode() and equals() in Phone. hashCode() should return the id, as it is supposed to be unique.
Since you are concerned about replacing the item, here's how HashSet will help you :
Create an instance of your object.
Remove the object you want to replace from the set.
Add the new object (you created in step 1) back to the set.
Both these operations 2 & 3 are guaranteed in O(1) / constant time.
You don't need to maintain a map for this problem, that's redundant.
If you want to get the object from the collection itself and then modify it, then HashMap would be better, search is guaranteed in O(1) time.
Instead of a list, use a map with the Phone's id as the key. Then your code looks like this:
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Map<Integer, Phone> phoneMap = c.getPhoneMap();
phoneMap.put(p.getId(), p);
System.out.println(c);
}
If you take the object out from the collection and update its properties, it will get reflected in the same object in collection too.. Hence, you dont have to technically replace object after updating it.
As "Mike M." pointed out, you can use hashmap to retrieve the object quickly without iteration and update the object values.
If order matters to you, you can change Collection to List (Since you're always using an ArrayList anyway) and then:
int index = col.indexOf(phone);
col.remove(phone);
col.add(p, index);

Getting my Product object into an Array - Java

I am pretty new to coding. This is my first class and it's an intro to Java. I am stuck on one part of the assignment - not sure where to go.
I have an inventory program that has a Product class and a Stock class. The stock class needs to be able to use the product object in an array in methods to 1)tell if a product is in stock w. its sku. 2) return quantity with sku 3)add or remove product from Stock.
It sounds simple enough but I'm not understanding it. I've been searching the internet and reading my book for weeks to no avail so I thought I'd give this a try.
My product class contains the usual get/set methods for qty, sku, price, name
This is my Stock class:
public class Stock
{
private static final int MAX = 100;
int currentNoOfProd = 0;
Product[] productsArray = new Product[MAX];
//I need an empty stock array constructor
/* public Stock (int[] stockArray)
{
this.productsArray = stockArray;
}*/
//method to tell if Product is in Stock with SKU
public void inStock()
{
for(int i = 0; i< MAX; i++)
{
System.out.println("testing stock inventory\n" + productsArray[i].getSKU());
if (productsArray[i].getQty() > 0)
{
System.out.println("In Stock");
}
}
}
//return the quantity of a Product given its SKU
public void qtyInStock()
{
for(int i=0; i< MAX; i++)
{
System.out.println("in qtyInStock loop\n" + productsArray[i].getSKU());
System.out.println("getting quantity qtyInStock" + productsArray[i].getQty());
}
}
//add or remove a Product from Stock
}
My stock class is just me testing ideas to try and do /something/ but at this point I'm completely lost. This may be vague but I don't know how much more specific I can get.
I am hard coding the sku, name, price, and quatity in my driver program, if that helps, when I initiate a product object.
Suggestions would be wonderful or tips, anything really to help me move along and figure this out and learn. Thanks so much.
Some pointers that hopefully guide you forward:
1. Instance variables in the Stock class
By default, you should always use the "least visible" scope, i.e. private, in your instance variables. This means that Product[] productsArray should be replaced with private Product[] productsArray. Variables in the private scope can only be accessed by the containing class, which is a good thing when the class is a part of a larger application and you are debugging things.
2. Method telling whether a product is in stock or not
Your inStock() method is currently neither using a "SKU" (Stock keeping unit) for input nor returning anything back to the caller. It would seem better to change the method signature to
public boolean inStock(String sku)
instead. The iteration over productsArray looks ok, even though you can make it even more concise using the for-each construct, which has been available from Java 5 onwards (the array knows its size after it has been created):
for (Product p : productsArray) {
// you can use p.getQty() and p.getSKU() within the loop
...
}
Please note that because your array can/will contain null elements, you should check for p != null before invoking any methods on p. Basically you just need to return true from the loop when you find a match with a positive quantity and the given "SKU". Otherwise return false at the end of the method because no matches were found.
3. Return quantity of a product
The method signature should be changed to
public int qtyInStock(String sku)
If we assume that productsArray only holds one Product for a given "SKU", then you can simply iterate over the array and return the quantity of a product if one is found.
4. Add or remove product from Stock.
You probably want to split this requirement into two different methods: one for adding and one for removing stuff from your stock. For example:
public void addProduct(Product p)
and
public void removeProduct(Product p)
These are more difficult methods to implement, because they require manipulating the underlying productsArray. Also, in a real life application, the method would possibly throw an Exception if something unexpected happens (stock out of space, product not in stock etc.).
If we assume that your MAX array size is large enough, and that you only store one Product per "SKU" in the array, then basically you need to:
When adding a product: Iterate over the array and try to find a product with a "SKU" matching the input product's "SKU". If a match is found, increment the quantity based on the quantity of the input product; else store the product in the first available slot (having productsArray[i] == null) in the array.
When removing a product: if a match is found in the array, just set it to null using productsArray[i] = null.
Note that in these array-modifying methods, it is more practical to iterate using the for loop that keeps track of the index (for (int i = 0; i < MAX; i++)), which you already used in your example code.
You should use a Collection, e. g. ArrayList<Product> instead of Product[]. Your stock would not have fixed size and you will get some useful API as for example list.add(product), list.contains(product) etc.

How do I add a User Input of type String to an ArrayList in Java?

I am attempting to make a course registration system and one of my classes (Course) is centered around course attributes (ie. Course number, course name, instructors, students). I am making an ArrayList so that the Administrator (one of the user types) may add as many instructors to the course as he/she would like- I have created a Scanner and a String variable and everything, but when I write the .add command, Eclipse highlights ".add" and says "the method .add() is undefined for the type of scanner". Now, I can understand this, but I have no idea how to fix it and I've tried so many ideas.
Here is the method:`
public static String Instructor(){
String courseInstructors;
System.out.println("Please add name(s) of course instructors.");
ArrayList<String> Instructors= new ArrayList<String>();
Scanner courseInst = new Scanner(System.in);
courseInstructors = courseInst.next();
//courseInst.add(courseInstructors);
for(String courseInstructors1 : Instructors) {
courseInstructors1 = courseInstructors;
courseInst.add(courseInstructors1);
}
return;
}`
Please adhere to Java naming conventions ad use lower case for variable names - instructors instead of Instructors.
Also, you want to add to your arraylist, so call add() on
instructors.add(courseInstructors1)
You may also want to consider choosing better variable naming than courseInstructors1, for instance just courseInstructor, since you are referring to on instructor of all instructors.
Also in your for loop you are doing the following
for(String courseInstructors1 : Instructors) {
courseInstructors1 = courseInstructors;
courseInst.add(courseInstructors1);
}
This can be simplified to
for(String courseInstructors1 : Instructors) {
courseInst.add(courseInstructors);
}
And if you look at the simplification you will see that iterating through Instructors make no sense here, since you are not using the contents of courseInstructors1.
I'm trying to understand what your loop is for.
if you are trying to get multiple instructor names from one input then you need something like this.
//get input
//"John Peggy Adam blah blah"
courseInstructors = courseInst.next();
//split the string by white space
String[] instArr = courseInstructors.split(" ");
//will give array of John, Peggy, Adam, blah, blah
Then do your foreach loop to add them to the list.
for(String inst: instArr){
instructors.add(inst);
}
Otherwise I would suggest doing something like this so you don't have to worry about splitting names and such.
courseInstructor = courseInst.nextLine();
while(!courseInstructor.equals("done"){
//add name to list of instructors.
instructors.add(courseInstructor);
//get next name.
courseInstructor = courseInt.nextLin();
//if the user types done, it will break the loop.
//otherwise come back around and add it and get next input.
}

Calling an Instance method when user inputs the instance name as a String

In a small project I am working on I've gotten stuck. The user enters a command that may be "xp Speed", my command handler class finds that it wants to the XP value of the Speed Instance. In this case it needs to return the value of Skill.Speed.currentXP back to the user.
Small Part of the program:
//Example Instance initialization there is over 40 of these
Skill Speed = (new SkillSpeed(Skills.SKILL_SPEED,Skills.SKILL_SPEED_MODIFIER));
//Constructor for skill class
public Skill(String skillName, double modifier) {
this.name = skillName;
this.minLevel = Skills.MIN_SKILL_LEVEL;
this.Modifier = 1f;
this.currentLevel = (int)calculateLevel();
this.currentXP = 1;
this.leaderboard = getCurrentLeaderboard();
this.ID = getNextID();
}
Now, theres one way i could do this. by having a switch statement with case value being the string entered. However I'm sure having 40+ cases in one switch statement must be avoidable. The other theory I have had is creating a array of all current instances then iterating through that list, finding if the user inputted string is equal to the name of that instance, then returning the instance itself. This is what I came up with:
//method inside another classs that attempts to return the appropriate skill Instance
public Skill getSkillFromName(String Name) {
for(int i = 0; i < Skill.SkillArray.length; i++) {
final String SkillName = Skill.SkillArray[i].getName();
if(SkillName.equalsIgnoreCase(Name)) {
return Skill.SkillArray[i];
}
}
return null;
}
So here's what I need help with:
Creating a array of all initialized instances
Creating the method that will return Skill."InsertRandomInstanceDependingOnUserInputHere".currentXP
Fixing any problems you see in the getSkillFromName() method
Or perhaps I have overlooked a far easier way of doing this, and you can help me with that.
Thanks for the help,
BigDaveNz
If the names of the skills excatly match method names you might find the aswer at "How do I invoke a Java method when given the method name as a string?".
For finding instances by name you can still use Map's.
You can use a Map for this. E.g.:
Map<String, Skill> skills = new HashMap<String, Skill>();
To insert the values you put the values into the Map:
skills.put(skill.getName(), skill);
To retrieve your skill you can get the skill by name:
Skill skill = skills.get(name);

Coding practice - Which coding approach should I use?

Sorry for such a vague title. Did not think of good one.
Situation:
Have a List of User objects.
Need to create array for UserInfo object.
UserInfo object is created is based on information in User object. (Currently has a method for this)
Which is better in such a situation?
Should I pass whole list of User to User to UserInfo conversion method.
or Should I loop over list of User and pass each user object to conversion method and get UserInfo for it.
Examples:
List<User> users = .....;
UserInfo[] userInfos = getUserInfoFromUser(users); //(conversion method will loop and generate array, then return it.)
or
List<User> users = .....;
UserInfo[] userInfos = new UserInfo[users.size()]
for (int j = 0; j < users.size(); j++) {
userInfos[j] = getUserInfoFromUser(users.get(j));
}
In first approach we pass a big object(list of User) as an argument and in second we call same method multiple times.Which is better?
The size of User list will be range from 25-200 objects in it.
How about having two conversion methods, one that takes a User and returns a UserInfo (this could and probably should be a constructor of UserInfo), and one that takes a List, does the looping and internally calls the first one?
The size of the list is not relevant.
I think it depends on how often you will doing this, because you don't want to repeat the same loop in multiple places in your code.
I would suggest creating two methods, one which returns the info for a single user and the other which returns info for a list of users:
public UserInfo[] getInfoForUsers(List<User> users) {
UserInfo[] userInfos = new UserInfo[users.size()];
for (int j = 0; j < users.size(); j++) {
userInfos[j] = getInfoForUser(users.get(j));
}
return userInfos;
}
public UserInfo getInfoForUser(User u) {
}
I prefer the first approach as it is simple, also, argument will be the address of the users object. So doesn't matter big or small in that case.
In the both cases, Java passes reference to an object. In 1st case, it's reference to collection of Users and in 2nd to an User.
I would recommend to use the first option: pass the whole array!
Reducing the number of function calls definitely pays out.
There is no difference: in the first version the function implementation would do the second version. Furthermore probably would like to have a function User -> UserInfo.

Categories

Resources