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.
Related
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);
This subclass should be able to let the user choose a specific employee ID, type it into the command line and choose to either add it to the array list, delete it from the array list or simply request to see more information about the specific employee ID. I've tried so many things with no luck at all.
package WorkIDServerStorage;
public class EmployeeList{
private Employee[] theEmployee;
private int arrayEmployee;
public EmployeeList(){
theEmployee = new Employee[100];
arrayEmployee = 0;
}
public EmployeeList(int arraySize){
theEmployee = new Employee[arraySize];
arrayEmployee = 0;
}
public void setTheEmployee(Employee[] inputTheEmployee){
theEmployee = inputTheEmployee;
}
public void setArrayEmployee(int inputArrayEmployee){
arrayEmployee = inputArrayEmployee;
}
public Employee[] getTheEmployee(){
return theEmployee;
}
public int getArrayEmployee(){
return arrayEmployee;
}
public Employee addEmployeeID(Employee employeeAdd){
return theEmployee[arrayEmployee++] = employeeAdd;
}
public Employee deleteEmployeeID(int employeeDelete){
//Delete an employee record with a
//specified record number from the array
}
public Employee readEmployeeInfo(int employeeRead){
//Read the employee data for a specified record number
//From the array and display this data to the screen
}
#Override
public String toString(){
StringBuilder sb = new StringBuilder();
for(int x = 0; x < arrayEmployee; x++){
sb.append(theEmployee[x].toString()).append("\n");
}return sb.toString();
}
}
Arrays are fixed length data structures. They are much like a multi-storied building. You can't take a floor out from the middle of the building and expect it to stand.
ArrayList (as others have pointed out), are dynamic structures, much like a train. You can take out compartments as you wish, you can reconfigure it.
I would in fact recommend NOT using a List at all, but a Map that maps an ID to an employee record. Let us say you have the following employees in a list -
Alice
Bob
John
Ruth
If you delete "Bob" the ID's for John and Ruth are going to change. Not a good practice. If you use a Map instead, everyone can keep their assigned ID's and you just add to the map by incrementing the keys (or IDs).
Hope this helps.
For being able to dynamically adding or removing from an array, you should use List, or ArrayList. Typical arrays don't provide delete or add at runtime, since they are fixed-size and if you want to control the procedure by handling the indices or other tricks, you'll probably end up in a messy and hard to maintain code.
On the other hand, Lists in java are dynamically sized and provide add(), remove(), get() and other convenient methods, which in my opinion best suits you.
You should use an ArrayList in order to dynamically add and remove entries in your array.
Make your theEmployee array into an ArrayList by doing:
ArrayList<Employee> theEmployee = new ArrayList<Employee>();
You can add and remove by doing:
public boolean addEmployeeID(Employee employeeAdd){
return theEmployee.add(employeeAdd);
}
public Employee deleteEmployeeID(int employeeDelete){
return theEmployee.remove(employeeDelete)
}
I changed the return type of addEmployeeID to boolean because an ArrayList returns a boolean when you add an object.
For remove, int employeeDelete would be the index of the object in the array. When you remove an object, all the remaining objects get shifted to the left. So if you have an array [1,2,3] and you remove 2, it would become [1,3]. The indexes are moved.
You could also remove an object so this should work:
public boolean deleteEmployeeID(Employee employeeDelete){
return theEmployee.remove(employeeDelete)
}
Where employeeDelete is an Employee and the function returns a boolean.
There is a HashMap called flights and an ArrayList called planes. The method is meant to create a new flight if the user enters the name of a plane that exists in this.planes. The only problem is that, with this method, there is no way to create two flights with the same plane and different arrival/departures. When you create a new Flight object with the same plane as a previous one, it REPLACES that previous one. I'm wondering how I can keep that from happening.
public void addFlight(String planeID, String departure, String arrival) {
Flight newFlight = null;
for(Plane p : this.planes) {
if(p.getID().equals(planeID)) {
newFlight = new Flight(p, departure, arrival);
}
}
flights.put(planeID, newFlight);
}
Since the planeID String being entered determines the plane from this.planes that is assigned to the new Flight, I made a loop that runs through the planes ArrayList to check if the name matches. I'm pretty sure this is what's keeping me from being able to make multiple "Flights" that have the same "Plane" but I don't know what else I can change. I tried adding in this additional if statement but it didn't do what I thought it would.
else if (this.flights.keySet().contains(p.getID())) {
newFlight = new Flight(p, departure, arrival); //In case it is a repeated Plane
}
You need this
HashMap<String,List<Flight>>
instead of
HashMap<String,Flight>
This will help you maintain more than one Flight instances in the list mapped with the planeID
Flights is a HashMap. This is a data structure that stores items in key/value pairs. In your case, planeID is the key. So when you are inserting a new flight, it will check the map's contents, see the existing Plane ID & insert your new flight in it's place. It's how the data structure works.
You need to use something more unique as the key. An object combining the plane ID & some departure details perhaps?
Create HashMap for planes and ArrayList of flights in stead.
Your design will much more be simplified and you'll get the expected behavior.
Adding code would become
public void addFlight(String planeID, String departure, String arrival) {
flights.add(planes.get(planeID), departure, arrival);
}
No need to search, check or worry.
Good luck.
Edit in response to the comment:
You are using plane id's as keys to the HashMap. So whichever is unique w.r.t. the key, should be placed in the HashMap. Which in this case is plane.
For storing flights, there were two choices
Aggregation: HashMap<Plane, List<Flight>> as suggested by Octopus
Association: List<Flight> with Plane as a member of Flight class.
Both choices are easy to use, but in first choice, your plane reference inside flight class is redundant. So I'd go for second choice.
You should choose whichever suits you best.
Hope this helps.
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);
I am fairly new to object oriented programming, so I am still having some trouble grasping some of the basic concepts. So here I am trying to create a basic inventory program to keep track of stocks. Each stock contains couple details: company name, stock rating (AAA, AAa, Aaa, stuff like that), purchase price and numbers of shares. The program will ask user to input these details through command line prompt. And users can only input at most 12 stocks. If the user enters a stock twice, it will print out an error. And if the user has inputted one stock twice, it will also print out an error message.
Here is what I have done so far: Stock object
public class Stock {
private String companyName;
private String stockRating;
private int price;
private int numberOfShares;
public String getCompanyName() {
return companyName;
}
public int getStockRating() {
return stockRating;
}
public String getPrice() {
return price;
}
public int getNumberOfShares() {
return numberOfShares;
}
public Stock(String companyName, String stockRating, int price, int numberOfShares) {
super();
this.companyName = companyName;
this.stockRating = stockRating;
this.price = price;
this.numberOfShares = numberOfShares;
}
Now, I am trying to create stock inventory program
import java.util.*;
public class StockInvetory {
private static final int INVENTORY_SIZE = 12;
private Stock [] stocks;
public StockInvetory() {
stocks = new Stock [INVENTORY_SIZE];
}
private static void StockInventory() {
for (int i = 0; i<INVENTORY_SIZE; i++){
Scanner console = new Scanner(System.in);
System.out.println ("Stock's name:");
String stockName = console.next();
System.out.println ("Stock's rating");
String stockRating= console.next();
System.out.println ("Stock's price:");
int stockPrice = console.nextInt();
System.out.println ("Numbers of shares: ");
int numberShares= console.nextInt();
stocks [i]= new Stock(stockName, stockRatings, stockPrice, numberShares);
}
public static void main (String [] args){
StockInventory();
}
}
So my questions are the following:
The first stock object program should be okay, my problem is the stock inventory program.
With this code, private Stock [] stocks, does it mean that the stock inventory program will store the info into an array? If it is, how do I store all the details associated with a particular stock, company name, price, etcs together. Do I have to create a multi-dimensional array in order of keep track of all the details? Like one row contains all the details about one stock, and second row contains details about another stock. And to determine if the user has entered one stock twice, do I use "equalsIgnoreCase" to do the comparison? I know I probably need to create another method for the stock Inventory.
Thank you very much in advance for your assistance.
Once you read in the name, rating, price, and share count, you need to call the constructor on your class Stock to create an instance of the class and assign it to the next item in your stocks[] array.
Like so:
stocks[0] = new Stock( stockName, stockRating, stockPrice, numberShares);
Then you'll need to put the lines of code that you're using to read from the console, plus my line that creates the new Stock object into a loop so that you can read in all 12 stocks:
for( int i = 0; i < INVENTORY_SIZE; i++ )
{
System.out.println ("Stock's name:");
String stockName = console.next();
System.out.println ("Stock's rating");
String stockRating= console.next();
System.out.println ("Stock's price:");
int stockPrice = console.nextInt();
System.out.println ("Numbers of shares: ");
int numberShares= console.nextInt();
stocks[i] = new Stock( stockName, stockRating, stockPrice, numberShares);
}
Now, this isn't perfect, since it will require users to always enter a full set of 12 stocks, so you'll need to figure out how to let the user abort out of the loop if they're done, and you'll still have to add that error checking you want, to ensure that no duplicates are entered, but it should initialize your individual objects and assign eachh one to the array elements.
private Stock [] stocks, does it mean that the stock inventory program
will store the info into an array? If it is, how do I store all the
details associated with a particular stock, company name, price, etcs
together.
Your array is like a list of stocks. But just because its one object, doesn't mean it only contains one piece of data. It can hold Strings, ints, and other user-defined data types. Therefore, you can store pieces of data relating to the stock inside the Stock object.
public class Stock {
private String companyName;
private String stockRating;
private int price;
private int numberOfShares;
Those are all stored in each Stock object, and can be accessed by the getter methods you defined.
int stockdata = stocks[4].getPrice();
However, to initialize your Stock array, you want to create a new Stock object for each area, like so:
for(int i = 0; i < INVENTORY_SIZE; i++) {
stocks[i] = new Stock(foo, bar, lorem, ipsum);
}
The variables used here are just placeholders, so you can create your parameters by reading from the console like you're doing above.
These design principles for OOP should help you understand the relationship between data and containers.
For the second part of your question, you can look at just comparing one of the data values, like companyName, or implementing an interface like Comparable.
for(Stock s : stocks) {
if(stockName.equals(s.getCompanyName()) {
// ERROR!
}
}
You may want to provide some way to break the loop in certain condition if user does not want to input all 12 information. However, it is totally on your requirements.
In that case, you may have to use other options like List or vector.
Each object can have multiple variables , like in your case rating, price ,name and share which are bound to each object. So when you store an object in an index of array, you are actually storing all those information contained by that object in one single place. So in this case, you do not have to create multidimensional array.
You can override your equals method based on what defines the object as equal and use it to determine if same stock is provided again. This will return true if both object are same, otherwise false.
You can post your updated code and log here so that you can get suggestion regarding your exception
In a real world scenario some questions come to mind:
price shouldn't be an integer because a price is usually an amount in a specific currency. Check this money related question.
Stock rating would be better represented using an Enum. See doc