DobromirM
DobromirM

Reputation: 2027

Updating an item in a Collection

I have a Collection of objects in Java and I need to get a specific item in order to update a property on it. I can get the item and update it using stream and filter or I can use .remove() and .add() to replace the old object.

My question is, which one of those will be better performance-wise and in general what are the pros and cons of each method.

e.g

public class Car
{
    private Integer id //unique;
    private String brand;
    private Float price;

    Car(Integer id, String brand, Float price)
    {
        this.id = id;
        this.brand = brand;
        this.price = price;
    }

    public Integer getId()
    {
        return id;
    }

    public void setId(Integer id)
    {
        this.id = id;
    }

    public String getBrand()
    {
        return brand;
    }

    public void setBrand(String brand)
    {
        this.brand = brand;
    }

    public Float getPrice()
    {
        return price;
    }

    public void setPrice(Float price)
    {
        this.price = price;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o)
        {
            return true;
        }
        if (o == null)
        {
            return false;
        }

        Car car = (Car) o;

        return id != null && id.equals(car.getId());
    }

    @Override
    public int hashCode()
    {
        return id != null ? id.hashCode() : 0;
    }

}


public static void main(String[] args)
    {
        Collection<Car> cars = new ArrayList<>();

        cars.add(new Car(1, "Ford", (float)10000));
        cars.add(new Car(2, "Fiat", (float)15000));
        cars.add(new Car(2, "BMW", (float)20000));

        //Method 1
        Car updateCar = new Car(2, "Fiat", (float)35000);
        Car newCar =  cars.stream().filter(c -> c.getId().equals(updateCar.getId())).collect(toList()).get(0);
        newCar.setPrice(updateCar.getPrice());

        //Method 2
        Car updateCar = new Car(2, "Fiat", (float)15000);
        cars.remove(updateCar);
        updateCar.setPrice((float)35000);
        cars.add(updateCar);
    }

Upvotes: 1

Views: 3883

Answers (5)

Holger
Holger

Reputation: 298123

Neither of your methods make much sense performance-wise

Method 1

    Car updateCar = new Car(2, "Fiat", (float)35000); // unnecessary object construction
    Car newCar =  cars.stream()
      .filter(c -> c.getId().equals(updateCar.getId()))
      .collect(toList()) // unnecessary List creation
      .get(0); // just to get the first element
    newCar.setPrice(updateCar.getPrice()); // to perform an action on it

A straight-forward solution would be

    Integer idToUpdate = 2;
    float newPrice = 35000;
    cars.stream()
        .filter(c -> idToUpdate.equals(c.getId()))
        .forEach(car -> car.setPrice(newPrice));

Method 2

    Car updateCar = new Car(2, "Fiat", (float)15000); // unnecessary use of old values
    cars.remove(updateCar);
    updateCar.setPrice((float)35000);// just to update the property
    cars.add(updateCar);

Since your Car’s equality is only based on the ID, there is no need to initialize a new Car instance with old properties.

Note that since you didn’t specify the collection type, we don’t know whether it may contain duplicates. So the straight-forward variant would be

    Car updateCar = new Car(2, "Fiat", 35000);        
    do {} while(cars.remove(updateCar));
    cars.add(updateCar);

or, to ensure that the number of occurrences doesn’t change:

    Car updateCar = new Car(2, "Fiat", 35000);
    int num = 0;
    while(cars.remove(updateCar)) num++;
    cars.addAll(Collections.nCopies(num, updateCar));

but this may still change the order of the collection’s elements (if the specific collection type has an order).

Obviously, the first variant is much simpler and retaining the properties of the source collection, which is more important than tiny performance differences. Unless the collection is a Set, both operations imply a linear overhead which can be avoided by using a map from id to instance instead.

Upvotes: 1

Yuvaraj Ram
Yuvaraj Ram

Reputation: 67

Both will not result in performance, since time complexity will be O(n). If you really look at performance point of view go with HashMap put and get. It will always give O(1)

Example:

Map put and get time complexity always O(1)

Map<Integer,Car> carsMap = new HashMap<Integer,Car>();
   carsMap.put(1, new Car(1, "Ford", (float) 10000));
   carsMap.put(2, new Car(2, "Fiat", (float) 15000));

   // Update Value of Car
   Car newCar = carsMap.get(2);
   newCar.setPrice((float) 35000);
   carsMap.put(2, newCar);

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148880

You proposed method 1 will not be accurate if the ids are not unique. For example if your tried to change the BMW, you will change the one for the Fiat because they have same id.

Anyway, the most performant way is likely to neither filter nor remove/insert but to update unless your objects are immutable, or you used a Set

And if you really care for performance, you should use unique ids and a Map instead of a mere Collection.

Upvotes: 1

guigolum
guigolum

Reputation: 131

solution 3 : use a Map to store each car by its ID. That's the goal of an ID, use it correctly.

Thus updating would simply require map.get(id).setPrice(newprice).


Another note : don't use collect() when you want to get one potential element. Streams have findFirst() or findAny() for parrallel methods. So

Car filtered = list.stream().filter(predicate).findFirst().get()

Would have better performances.

Upvotes: 1

assylias
assylias

Reputation: 328598

You are looking at it the wrong way: when thinking of performance, your point of entry should be time complexity. Then you can delve into implementation details.

In your case, both methods run in O(n).

If performance is that important, move to an O(1) algorithm, for example using a HashMap<Integer, Car.

Upvotes: 2

Related Questions