Increasing value of an object if it's already in a map - java

Every time when a product is added to shoppingBasket if its already in map basket it should increase it value amount by 1. But it doesn't for some reason. Is it because every time im adding a product to map i'm creating a new purchases? I can't figure it out.
public void add(String product, int price) {
Purchases buy = new Purchases(product, 1, price);
if(!basket.containsKey(product)) {
basket.put(product, buy);
} else {
buy.increaseAmount();
}
/
public void increaseAmount() {
this.amount+= 1;
}
/
public class Main {
public static void main(String[] args) {
ShoppingBasket basket = new ShoppingBasket();
basket.add("milk", 3);
basket.print();
System.out.println("basket price: " + basket.price() +"\n");
basket.add("buttermilk", 2);
basket.print();
System.out.println("basket price: " + basket.price() +"\n");
basket.add("milk", 3);
basket.print();
System.out.println("basket price: " + basket.price() +"\n");
basket.add("milk", 3);
basket.print();
System.out.println("basket price: " + basket.price() +"\n");
}
}
/
import java.util.HashMap;
import java.util.Map;
public class ShoppingBasket {
private Map<String,Purchases> basket;
public ShoppingBasket() {
this.basket = new HashMap<String,Purchases>();
}
public void add(String product, int price) {
Purchases buy = new Purchases(product, 1, price);
if(!basket.containsKey(product)) {
basket.put(product, buy);
} else {
buy.increaseAmount();
}
}
public int price() {
int price = 0;
for(Purchases item : basket.values()) {
price += item.price();
}
return price;
}
public void print() {
Map<String, Integer> test = new HashMap<String,Integer>();
for(Purchases item : basket.values()) {
test.put(item.product(), item.amount());
}
for(String key : test.keySet()) {
Integer value = test.get(key);
String complete = key + ": " + value;
System.out.println(complete);
}
}
}
/
public class Purchases {
private String product;
private int amount;
private int unitPrice;
public Purchases(String product,int amount, int unitPrice) {
this.product = product;
this.amount = amount;
this.unitPrice = unitPrice;
}
public int price() {
return this.amount * this.unitPrice;
}
public void increaseAmount() {
this.amount+= 1;
}
public String toString() {
return "" + this.amount;
}
public int amount() {
return this.amount;
}
public String product() {
return this.product;
}
}

In your else black, you need to retrieve the Purchase object from the map. Then call increaseAmount on the object retrieved.
public void add(String product, int price) {
Purchases buy = new Purchases(product, 1, price);
if(!basket.containsKey(product)) {
basket.put(product, buy);
} else {
buy = basket.get(product); <--retrieve it
buy.increaseAmount(); <--increment amount
}
}

code is not adding purchase to map if key is already there, add map.put in else part
public void add(String product, int price) {
Purchases buy = new Purchases(product, 1, price);
if(!basket.containsKey(product)) {
basket.put(product, buy);
} else {
buy.increaseAmount();
basket.put(product, buy);
}
}

Purchases buy = new Purchases(product, 1, price);
See the new? You're making a new object here, hence the name. You then increment the product count on this new object and promptly toss the only variable that refers to this new object (your buy variable) in the garbage, because that's what happens with all local variables when a method ends: The variable goes away. With that, nothing refers to this brand new Purchases instance and thus it'll eventually be garbage collected.
You want to query for the actual object that was made earlier and stored in that map, then increment the product count on that.
In your code, you make a new Purchases instance no matter what happens, and then map the given string to this newly created purchases object only if the string isn't already in your map. This is no good. You want to create a new purchases instance only if it isn't already in the map, otherwise you want to fetch the existing instance of Purchases.
You could do this:
Purchases buy;
if(!basket.containsKey(product)) {
basket.put(product, buy = new Purchases(product, 1, price));
} else {
basket.get(product).increaseAmount();
}
But this is inefficient, 'ugly' (kinda hard to maintain), and outright broken if this is a concurrent hashmap. Much better is to act-then-check instead:
basket
.computeIfAbsent(product, k -> new Purchases(product, 0, price))
.increaseAmount();
This code does just what it says: It will compute the requisite value, but only if there is no key/value mapping yet. So, IF product is in the map, you just get the Purchases instances associated with it and we move immediately on to .increaseAmount(). But if it is not, the code new Purchases(product, 0, price) is executed and whatever that resolves to is used as the value (So, it's like .put(product, new Purchases(...)), except the new part is only run if that item wasn't in the map already. There's a k -> in there because [A] it's a closure, it's code that is sent to the computeIfAbsent method and is only actually run if needed, and [B] you get the key passed along. Here, not needed, you already have this (variable product), but you can imagine you invoked perhaps basket.computeIfAbsent(some.complex().calculation(), ...., that's why it's there.
You then call increaseAmount() on whatever you get. Hence why this code starts out with an amount of 0: Because it'll be increment to 1 immediately afterwards.

This is because you increase the amount in the new Purchases object which is not in the map.
Let's do code analysis:
[Map State] EMPTY
You put one Purchases object with product's name/key "milk" inside the map:
public void add(String product, int price) {
Purchases buy = new Purchases(product, 1, price);
if(!basket.containsKey(product)) {
// Executed
basket.put(product, buy);
} else {
buy.increaseAmount();
}
}
As you probably know the code found in the if block is executed - Adds the object inside buy to the map.
[Map State] { "milk", PurchasesObject1 }
Now you are trying again to put a NEW Purchases object whose product's/key value is "milk" again into the map. As you probably know, the else block will be executed, Because Purchases object with key "milk" already exists in the map.
What happens in the else block is that you increase the value of amount of the NEW local object you've just created inside the method, which is not even in the map at all, so once the add() method finishes its execution, the object you've created becomes garbage that needs to be collected by the garbage collector.
Solution?
Sure. Just retrieve the object with the same key and do what you wanted on it.
public void add(String product, int price) {
Purchases buy = new Purchases(product, 1, price);
if(!basket.containsKey(product)) {
// Executed
basket.put(product, buy);
} else {
basket.get(product).increaseAmount();
}
}

Related

How to erase a current object using a function | Java

I am trying to make an erase function to delete the teams of the tournament using the team code (value c in the constructor). Firstly I want to check if that team exists in the objects I made in the main method. Is that possible to do that using an if statement?
Exercise:
Create a java application that stores data for various football teams. Each team has a name, a code and the current points in the championship. In your class create methods for registering new teams, erasing existing teams using their code and showing a list for all the teams currently active in the championship
package assignment.exercise4;
public class Data {
private String name = "";
private int code = 0;
private static int register;
private int erase;
private int currentpoints = 0;
public Data(int c, int points, String n) { //constructor
code = c;
this.currentpoints = points;
name = n;
}
public void Erase(int c)
{
code = c;
if(code != 0)
System.out.println("Team with Code: "+code+" has been erased" );
else
System.out.print("Team with code "+code+" does not exist!");
}
public void Register(String newTeam,int code)
{
name = newTeam;
this.code = code;
System.out.println("New Team " + name + " registered with code " + code);
}
public void print()
{
System.out.println("Team name: " + name + "\nTeam code: " + code + "\nTeam points: " + currentpoints + "\n");
}
}
/*
public static void main(String[] args) {
System.out.println("\nList of Teams: \n");
Data t1 = new Data(110,42,"Juventus");
Data t2= new Data(105,45,"Manchester City");
Data t3= new Data(240,50,"Barcelona");
Data t4= new Data(122,36,"Arsenal");
Data Team = new Data(0,0,""); //use for erase
t1.print();
t2.print();
t3.print();
t4.print();
System.out.println("Teams erased: \n");
Team.Erase(110);
Team.Erase(122);
Team.Erase(0);
System.out.println("\n\nTeams Registered: \n");
t1.Register("Real madrid", 11);
t1.Register("Atletico Madric", 112);
}
}
*/
What are you trying to erase the teams from?
If they were in a list, for example...
Data t1 = new Data(110,42,"Juventus");
Data t2= new Data(105,45,"Manchester City");
Data t3= new Data(240,50,"Barcelona");
Data t4= new Data(122,36,"Arsenal");
List<Data> teams = Arrays.asList(t1, t2, t3, t4);
...you could create a list with a team erased like this...
public List<Data> erase(List<Data> team, int id) {
return team.stream()
.filter(t -> t.getId() != id)
.collect(Collectors.toList());
}
So...
List<Data> remainingTeam = erase(team, 122); // Removes Arsenal
...would remove the first element from the list
I will not answer this to elaborately since it is homework. I will try to give you a hint though.
If you have a team and want to do something with it. Otherwise you just have a team which just stays there in a particular scope (if you do not know what scope is, look it up!). If you have a team you most likely want do do something with it. In this case you seem to want to store information about the teams to use in a championship. Important to note here is that the teams are not the focus here. The real focus is the Championship. The teams are just a part of the championship. There can still be a championship even if all teams does not choose to participate. But you want all teams choosing to participate to be registered to this particular championship (eg UEFA Champions League).
This leads to something called aggregate or association depending on how hard you want to tie the object to the championship. However you do probably not need to pursue these terms any further at this point. What is important to remember is that there is an "has a" relation between the championship and the teams. The championship "has a" collection of participating teams. This is normally reflected in this way in code,
public class Championship {
private Team[] teams; // Or List<Team>, Collection<Team>, HashMap<Team>, ...
}
The Championship can then have methods for registering a team, removing a team, updating status, etc...
public void register(Team t) {
if (numberOfTeams < teams.length) {
teams[numberOfTeams] = t; // Index starts at zero
numberOfTeams++;
} else {
throw new IndexOutOfBoundsException("The list is full. " +
"No more teams may be registered!")
}
}
Even though the function erasing a team was requested, I believe I will not write it down. This design is so different from your original intent, so that writing the erase function will likely solve your complete homework. However, you do actually not have to erase the team it is perfectly possible to just overwrite the position with the next team as,
teams[i] = teams[i+1];
Hope this helps!
Short answer:
public void erase(int id) {
// who needs an if statement, if we can use predicates?
teams.removeIf(team -> team.getId() == id);
}
But this will not work with your current code. Your current code misses the container for your teams.
Longer answer:
For the fun of it. Solving your homework:
class Team {
int id;
String name;
int points;
Team(int id, String name, int points) {
this.id = id;
this.name = name;
this.points = points;
}
#Override
public String toString() {
// ugly formatted... another homework? ;-)
return "Team '" + name + "' (" + id + "): " + points;
}
}
Note, that I will not add any getter or setter, nor will I care about visibility here. I will leave that as another homework for you.
class Championship {
List<Team> teams = new ArrayList<>();
void register(Team team) {
teams.add(team);
}
void erase(int id) {
teams.removeIf(team -> team.id == id);
}
#Override
public String toString() {
// for additional fun... sorted by descending points
return "=== Championship table ===\n"
+ teams.stream()
.sorted((o1, o2) -> Integer.compare(o2.points, o1.points))
.map(Objects::toString)
.collect(Collectors.joining("\n"));
}
}
Somewhere else:
public static void main(String[] args) {
Championship championship = new Championship();
championship.register(new Team(1, "not the best ones", 3));
championship.register(new Team(2, "The better ones", 7));
championship.register(new Team(3, "The winners", 11));
System.out.println(championship);
championship.erase(3);
System.out.println(championship);
}
Output:
=== Championship table ===
Team 'The winners' (3): 11
Team 'The better ones' (2): 7
Team 'not the best ones' (1): 3
=== Championship table ===
Team 'The better ones' (2): 7
Team 'not the best ones' (1): 3
Too much of information? Just start with something like a championship-class or at least use a collection of Teams (e.g. List<Team>).
By the way... Do not deliver this solution as your homework, except you understand what is going on and you can explain it with your own words. Otherwise you are only betraying yourself.

Throws Exception Error in Java

I'm working on a type of inventory program that deals with reading in a list of items from a file and I'm having some trouble.
Here's the code I have finished so far:
import java.util.Scanner;
import java.io.*;
public class Store {
public static void main(String[] args) throws Exception {
Book[] books = readInventory();
for (Book book : books) {
System.out.printf("ISBN: %s, Price: %f, Copies: %d%n",
book.getISBN(), book.getPrice(), book.getCopies());
}
}
public static Book[] readInventory() throws Exception {
Book[] books = new Book[15];
java.io.File file = new java.io.File("../instr/prog4.dat");
Scanner fin = new Scanner(file);
String isbn;
double price;
int copies;
int i = 0;
while (fin.hasNext()) {
isbn = fin.next();
if (fin.hasNextDouble()); {
price = fin.nextDouble();
}
if (fin.hasNextInt()); {
copies = fin.nextInt();
}
Book book = new Book(isbn, price, copies);
books[i] = book;
i++;
}
fin.close();
return books;
}
public static void printInfo(Book[] books) {
for(int x=0; x<books.length; x++) {
System.out.println("ISBN: " + books[x].getISBN() + "\n Price: " +
books[x].getPrice() + "\n Copies: " + books[x].getCopies());
}
}
}
class Book {
private String isbn;
private double price;
private int copies;
public Book(String isbnNum, double priceOfBook, int copiesInStock) {
isbn = isbnNum;
price = priceOfBook;
copies = copiesInStock;
}
public String getISBN() {
return isbn;
}
public double getPrice() {
return price;
}
public int getCopies() {
return copies;
}
public void setISBN(String isbn) {
this.isbn = isbn;
}
public void setPrice(double price) {
this.price = price;
}
public void setCopies(int copies) {
this.copies = copies;
}
#Override
public String toString() {
return String.format("ISBN: %s, Price: %f, Copies: %d%n",
this.getISBN(), this.getPrice(), this.getCopies());
}
}
The program compiles fine, but when I run the program I get the error
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:855)
at java.util.Scanner.next(Scanner.java:1364)
at Store.readInventory(Store.java:31)
at Store.main(Store.java:13)
Here is the contents of the file I am supposed to use:
1234567 31.67 0
1234444 98.50 4
1235555 27.89 2
1235566 102.39 6
1240000 75.65 4
1247761 19.95 12
1248898 155.91 0
1356114 6.95 17
1698304 45.95 3
281982X 31.90 5
I have been looking at another program I wrote recently that dealt with reading files to see that I was throwing the exceptions in the same types of places and it appears that I am (everything dealing with the file in that program was in the main method so that was the only one throwing an exception). From what I remember from an earlier Java programming class, we were supposed to throw exceptions at the main method and any other methods that deal with the file, so in this case I have the one on main and another on my readInventory() method.
What am I missing?
Check for next value before reading to avoid such exception as you are doing for isbn but not for price and copies.
sample code:
if(fin.hasNextDouble()){
price = fin.nextDouble();
}
if(fin.hashNextInt()){
copies = fin.nextInt();
}
For example Scanner#nextInt() method throws:
InputMismatchException - if the next token does not match the Integer regular expression, or is out of range
NoSuchElementException - if input is exhausted
IllegalStateException - if this scanner is closed
You are creating array of size 15 where is there is only 10 line in file that result in NullPointerException. Use List in that case.
You can use Java 7 - The try-with-resources Statement for better resource management even in case of error.
sample code
public static BookDetail[] readInventory() throws Exception {
List<BookDetail> books = new ArrayList<BookDetail>();
java.io.File file = new java.io.File("../instr/prog4.dat");
try (Scanner fin = new Scanner(file)) {
String isbn;
double price = 0;
int copies = 0;
while (fin.hasNext()) {
isbn = fin.next();
if (fin.hasNextDouble()) {
price = fin.nextDouble();
}
if (fin.hasNextInt()) {
copies = fin.nextInt();
}
BookDetail book = new BookDetail(isbn, price, copies);
books.add(book);
}
}
return books.toArray(new BookDetail[books.size()]);
}
Currently, you are checking if the file has more data, then you attempt to read 15 books, no matter what. Your for-loop goes from 0 to books.length-1, whatever is in the file.
Since you don't know (in theory) how many books there are in the file, you need to check fin.hasNext before reading each book.
So get rid of your for-loop and replace your reading code with something like this:
int i = 0;
while (fin.hasNext()) {
// read book here and assign to books[i]
// increment i for next iteration
i++;
}
The while condition fin.hasNext() will be checked before reading each book. When it returns false, you'll stop looping, and i will be the number of books you read.
Note that you're still assuming that each book has all its fields. If there was only half a book in the file, your code would still crash. But that's probably a good thing, since there's not really a way you can recover from that, and presumably you can assume you will receive only well-formed input.
I would suggest you use exception handling. I especially recommend using try with resources, since you forget to close the scanner that you are using.
You need to check in the code that you receive all the information that you need for each book. You don't want to have only partial information on the book just because you couldn't receive the rest of the data on that particular book.

TreeSets and removing specific unnamed Objects

So I'm writing a program for an assignment where I store Patients into a TreeSet. The problemn I'm having is I have to implement a method to discharge a specefic patient from the TreeSet.
for(int i = 0; i < 10 ; i++){
Random ag = new Random();
int age = ag.nextInt(99) + 1;
Names randomname = Names.getRandom();
String name = randomname.name();
String sex;
if(Math.random() > 0.5)sex = "female";
else sex = "male";
Random sn = new Random();
int serial = sn.nextInt(10000) + 1;
Address randomAddress = Address.getRandom();
String address = randomAddress.name();
Hospital.admitPatient(new Patient(age, name, sex, serial, Birthday.produceBirthday(), address));
}
So Thats how I am looping to get the Patients info and stats for the Patient Object. The admit patient method adds them to the TreeSet.
public static void admitPatient(Patient obj){
if(numofPatients < maxPatients){
patientList1.add(obj);
}
}
The Problem I'm having is withbthe Discharge patient method. Where I don't know what to put in the method
public static void dischargePatient(What do i put here in the driver when i call this method?){
patientList1.remove(w/e i put up there);
}
Since I didn't name the Objects of patients when creating them but just inserted them straight into the TreeSet I'm not sure exactly how to call them when i call the discharge patient method.
As you usually want to work with selected objects (patients) and not the whole list, you need a way to identify them somehow (for example by name or ID).
Since add and remove are similar, your dischargePatient method will be similar as well. Try
public static void dischargePatient(Patient patient) {
patientList1.remove(patient);
}
To retrieve a patient with a certain ID, you may iterate through your set and return it:
public Patient getPatientByID(String id) {
for (Patient patient : patientList1) {
if (patient.getID().equals(id)) {
return patient;
}
}
}
To remove a patient with ID "1234abc", you could do the following:
dischargePatient(getPatientByID("1234abc"));
Using this pattern, you rebuild the functionality of the map datastructure. Thus it might be better to use a Map (e.g. HashMap<>). Code will be reduced to operations like:
Map<String, Patient> patients = new HashMap<>();
patients.put("1234abc", patient1);
patients.remove("1234abc");
Full code for your example:
public static void admitPatient(Patient patient) {
if(numofPatients < maxPatients){
patients.put(patient.getID(), patient);
}
}
public static void dischargePatient(String id) {
patients.remove(id);
}

Arrays.sort on 2-dimensional array without triggering Garbage Collection?

This code works perfectly, but unfortunately it triggers garbage collection because of the Arrays.sort() Comparator.
Is there a way to do this that won't trigger Garbage Collection?
(NOTE: This code has been modified to be more "generic". The actual code is for an Android game, which is why Garbage Collection-induced slowdown is an issue.)
static final byte INCOME = 0;
static final byte INDEX = 1;
public void vSortEmployees() {
nPaidEmployees = 0;
for (nIter=0; nIter<MAX_EMPLOYEES; nIter++) {
if ((employees[nIter].current == true) && (employees[nIter].volunteer == false)) {
// We have another current and paid employee; add that employee's "amount earned to date" to the list.
paidemployees[nPaidEmployees][INCOME] = employees[nIter].fGetTotalIncomeToDate();
paidemployees[nPaidEmployees][INDEX] = nIter;
nPaidEmployees++;
}
}
Arrays.sort(paidemployees, new Comparator<float[]>() {
#Override
public int compare(float[] f1, float[] f2) {
if (f2[INCOME] < f1[INCOME])
return -1;
else if (f2[INCOME] > f1[INCOME])
return 1;
else
return 0;
}
});
// Now we have a list of current, paid employees in order of income received.
// Highest income paid out
paidemployees[0][INCOME]
// Second highest income paid out
paidemployees[1][INCOME]
// If we need to reference the original employee object, we can:
employees[paidemployees[0][INDEX]].getName();
}
There is not way to consistently trigger or not to trigger GC. GC lives its own life. The fact that it runs when you are sorting your array does not mean anything.
But however you probably can do something. Just do not user anonymous inner class for comparator. You do not really need this. Use regular class and create its object as a singleton. Then just use this instance. In this case no new objects will be created in your code during the sort and GC probably will not run.
class FloatArrayComparator implements Comparator<float[]>() {
#Override
public int compare(float[] f1, float[] f2) {
if (f2[INCOME] < f1[INCOME])
return -1;
else if (f2[INCOME] > f1[INCOME])
return 1;
else
return 0;
}
};
class SomeClass {
private Comparator<float[]> floatArrayComparator = new FloatArrayComparator();
void myMethod() {
Arrays.sort(myArray, floatArrayComparator);
}
}

Incrementing value in a Map (JAVA)

I'm having a problem with this home work assignment. I can handle the syntax, but not the logic. Could you provide me some tips.
What I'm trying to achieve is that the add method would increase the amount of products by using the increaseAmount method. Now it resets the value of amount to "1" every time I call the method. What makes this complicated is that I'm not allowed to use any other private variables than already used.
private Map<String, Purchase> shoppingCart = new HashMap<String, Purchase>();
public void add (String product, int price) {
Purchase purchase = new Purchase (product, 1, price);
//the line abowe returns the value back to 1 which I don't want.
if(shoppingCart.containsKey(product)) {
shoppingCart.put(product, purchase);
purchase.increaseAmount();
}
else {
shoppingCart.put(product, purchase);
The product constructor:
public Ostos(String product, int amount, int price) {
Code for the increaseAmount method:
private int amount;
public void increaseAmount() {
this.amount = (this.amount + 1);
Don't create a new purchase at the beginning only create it if it's not already there
public void add (String product, int price) {
if(shoppingCart.containsKey(product)) {
Purchase purchase = shoppingCart.get(product);
purchase.increaseAmount();
//You might have to put the purchase back into the cart I'm not sure
}
else {
Purchase purchase = new Purchase (product, 1, price);
shoppingCart.put(product, purchase);
}
You have to retrieve the value from shoppingCart and then increment the amount. You're never calling shoppingCart.get, so you're replacing the value each time by blindly putting the new purchase object into the map.

Categories

Resources