Reputation: 2027
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
Reputation: 298123
Neither of your methods make much sense performance-wise
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));
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
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
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
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
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