如果对象已经在地图中,则增加对象的值

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

每次当一个产品被添加到 shoppingBasket 时,如果它已经在地图篮中,它应该将它的值增加 1。但由于某种原因它没有。是因为我每次将产品添加到地图时都会创建新的购买吗?想不通了。

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;
    }
}

在你的 else black 中,你需要从地图中检索 Purchase 对象。然后在检索到的对象上调用 increaseAmount

    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
        }
    }

如果密钥已经存在,代码不会将购买添加到地图,请在其他部分添加 map.put

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);

看到new了吗?您在这里创建了一个 new 对象,因此得名。然后,您增加这个新对象的产品计数,并立即将引用这个新对象的唯一变量(您的 buy 变量)扔进垃圾箱,因为当方法结束时,所有局部变量都会发生这种情况:变量消失了。这样一来,没有任何内容指向这个全新的 Purchases 实例,因此它最终将被垃圾收集。

您想查询之前制作并存储在该地图中的实际对象,然后增加其上的产品计数。

在您的代码中,无论发生什么情况,您都创建一个新的 Purchases 实例,然后仅当给定字符串不在您的映射中时才将该字符串映射到这个新创建的购买对象。这可不行。您只想创建一个新的购买实例,如果它不在地图中,否则您想要获取现有的购买实例。

你可以这样做:

Purchases buy;
if(!basket.containsKey(product)) {
  basket.put(product, buy = new Purchases(product, 1, price));
} else {
  basket.get(product).increaseAmount();
}

但这是低效的,'ugly'(有点难以维护),如果这是并发哈希图,则完全损坏。更好的做法是先行动再检查:

basket
  .computeIfAbsent(product, k -> new Purchases(product, 0, price))
  .increaseAmount();

此代码如其所说:它将计算必要的值,但前提是还没有 key/value 映射 。因此,如果 product 在地图中,您只需获取与其关联的 Purchases 实例,我们立即转到 .increaseAmount()。但如果不是,则执行代码 new Purchases(product, 0, price) 并将解析为的任何内容用作值(因此,它就像 .put(product, new Purchases(...)),除了新部分只是 运行 如果该项目地图上还没有。那里有一个 k ->,因为 [A] 它是一个闭包,它是发送到 computeIfAbsent 方法的代码,如果需要,实际上只是 运行, [B] 你得到了传递的密钥。在这里,不需要,你已经有了这个(变量 product),但你可以想象你可能调用了 basket.computeIfAbsent(some.complex().calculation(), ....,这就是它在那里的原因。

然后,无论你得到什么,你都会打电话给 increaseAmount()。因此,为什么这段代码以 0 开始:因为它会在之后立即增加到 1。

这是因为您在地图中没有的新 Purchases 对象中增加了 amount


我们来做代码分析:

[地图状态]空

你在地图中放置了一个 Purchases 对象 product 的 name/key "milk":

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();
   }
}

您可能知道执行 if 块中的代码 - 将 buy 内的对象添加到地图。

[地图状态] { "牛奶", PurchasesObject1 }

现在您再次尝试放置 NEW Purchases 对象,其 product's/key 值再次为 "milk"进入地图。您可能知道,else 块将被执行,因为 Purchases 键为 "milk" 的对象已经存在于映射中。

在 else 块中发生的事情是您增加了您刚刚在方法中创建的 NEW 本地对象 amount 的值,这不是即使在地图中,所以一旦 add() 方法完成执行,您创建的对象就会变成垃圾,需要由垃圾收集器收集。


解决方案?

当然可以。只需检索具有相同密钥的对象并在其上执行您想要的操作即可。

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();
   }
}