Java update set value

Updating an object within a Set

So checking if it is in there is easy enough ( contains ), and adding to the set is easy too. My question is this: how do I get a handle to update the object within? Interface Set doesn’t have a get method, and the best I could think of was to remove the object in the set and add mine. another, even worse, alternative is to traverse the set with an iterator to try and locate the object.

I’ll gladly take better suggestions. This includes the efficient use of other data structures.

EDIT: Thank you all for answering. Unfortunately I can’t ‘accept’ the best answers here, those that suggest using a Map , because changing the type of the collection radically for this purpose only would be a little extreme (this collection is already mapped through Hibernate. )

One important note here: if you define equals, and you’re using a Set, you MUST MUST MUST define hashCode as well, so that items that compare equal have the same hashCode.

If you can answer the question in my answer about whether you need more fields than just id and b, I can edit my answer to include just the code you need.

The java.util.Set API exposes «contains». You need to use something like: A a = new A(id,b); if(set.contains(a)) a.update(. ); else set.add(a);

I think you’re asking the wrong questions here. To me this looks a bit odd. It look as if you’re trying to compare detached entities of type A to attached entities of the same type. This indicates that your domain model is either incomplete or wrong. Still, you might be able to circumvent these problems by using simple transactions.

Читайте также:  Перезагрузка страницы через php

7 Answers 7

Since a Set can only contain one instance of an object (as defined by its equals and hashCode methods), just remove it and then add it. If there was one already, that other one will be removed from the Set and replaced by the one you want.

I have code that does something similar — I am caching objects so that everywhere a particular object appears in a bunch of different places on the GUI, it’s always the same one. In that case, instead of using a Set I’m using a Map, and then I get an update, I retrieve it from the Map and update it in place rather than creating a new instance.

That’s not what ‘add’ does. If the set contains an equal object, ‘add’ return false. See java.sun.com/javase/6/docs/api/java/util/Set.html#add(E)

I changed it to «remove then add». It’s still a valid way of doing what you said you wanted, even if it’s not what you meant.

Agree with @Yuval, Set will not replace old object with new one, it will keep old one there only and won’t update it.

You really want to use a Map , not a Set .

Then map the ID (even though it’s also stored in A !) to the object. So storing new is this:

A a = . ; Map map = new HashMap(); map.put( a.id, a ); 

Your complete update algorithm is:

public static void update( Map map, A obj )

However, it might be even simpler. I’m assuming you have more fields than that in A that what you gave. If this is not the case, just using a Map is in fact what you want, then it collapses to nothing:

Map map = new HashMap(); // The insert-or-update is just this: map.put( id, b ); 

Obviously there are more fields in A. In fact, the update I mean to make is in of the fields within one of A’s fields! The key to A is not that simple either. =8-)

I don’t think you can make it any easier than using remove/add if you are using a Set.

If a matching A was found it will be removed and then you add the new one, you don’t even need the if (set.contains(A)) conditional.

If you have an object with an ID and an updated field and you don’t really care about any other aspects of that object, just throw it out and replace it.

If you need to do anything else to the A that matches that ID then you’ll have to iterate through the Set to find it or use a different Container (like the Map as Jason suggested).

@18Raabit I implemented this approach, but a ConcurrentModificationException is thrown by the HashMap

I wasn’t happy with how that code looked so I ended up doing this. stackoverflow.com/a/48740040/728602

Good answer. Anyway, it should be pointed out that iterating over the set’s elements would make the search cost O(n) instead of O(1).

No one has mentioned this yet, but basing hashCode or equals on a mutable property is one of those really, really big things that you shouldn’t do. Don’t muck about with object identity after you leave the constructor — doing so greatly increases your chances of having really difficult-to-figure out bugs down the road. Even if you don’t get hit with bugs, the accounting work to make sure that you always properly update any and all data structures that relies on equals and hashCode being consistent will far outweigh any perceived benefits of being able to just change the id of the object as you run.

Instead, I strongly recommend that you pass id in via the constructor, and if you need to change it, create a new instance of A. This will force users of your object (including yourself) to properly interact with the collection classes (and many others) that rely on immutable behavior in equals and hashCode .

What about Map I know it’s redundant, but I believe it will get you the behavior you’d like. Really I’d love to see Set have a get(Object o) method on it.

You might want to generate a decorator called ASet and use an internal Map as the backing data structure

class ASet < private Mapmap; public ASet() < map = new HashMap(); > public A updateOrAdd(Integer id, int delta) < A a = map.get(a); if(a == null) < a = new A(id); map.put(id,a); >a.setX(a.getX() + delta); > > 

You can also take a look at the Trove API. While that is better for performance and for accounting that you are working with primitive variables, it exposes this feature very nicely (e.g. map.adjustOrPutValue(key, initialValue, deltaValue).

Источник

Update HashMap values of type HashSet

I have trouble updating the HashMap values of Set type. After initializing the HashMap with key-values pair, I want to insert a new value to the the existing value Set, and the new value is to be incremented for each insert. My code is following:

 public static void main(String[] args) < String[] mapKeys = new String[]; Map> hashMap1 = new HashMap>(); Set values = new HashSet(); for (int i = 0; i < 10; i++) // initialize the values to a set of integers[1 through 9] < values.add(i); >for (String key : mapKeys) < hashMap1.put(key, values); >StdOut.println("hashMap1 is: " + hashMap1.toString()); int newValues = 100; // insert this newValues (incremented at each insert) to each value Set valuesCopy; for (String key : mapKeys) < if (hashMap1.containsKey(key)) < valuesCopy = hashMap1.get(key); // first, copy out the existing values valuesCopy.add(newValues++); // insert the newValues to the value Set hashMap1.remove(key);// remove the existing entries hashMap1.put(key, valuesCopy); // insert the key-value pairs >> StdOut.println("the updated hashMap1 is: " + hashMap1.toString()); > 

When executing the code, each HashMap key is associated with the same set of Integers: [102, 0, 1, 2, 100, 3, 101, 4, 5, 6, 7, 8, 9], however, what I really want is insert only one number to each set, this is what I want: [0, 1, 2, 100, 3, 4, 5, 6, 7, 8, 9] I need help understanding this: why all the new inserted values are the same? how to make it work as I wish? thanks for help

Источник

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

Источник

Replacing a HashSet Java member

I have Set of that structure. I do not have duplicates but when I call: set.add(element) -> and there is already exact element I would like the old to be replaced.

import java.io.*; public class WordInfo implements Serializable < File plik; Integer wystapienia; public WordInfo(File plik, Integer wystapienia) < this.plik = plik; this.wystapienia = wystapienia; >public String toString() < // if (plik.getAbsolutePath().contains("src") && wystapienia != 0) return plik.getAbsolutePath() + "\tWYSTAPIEN " + wystapienia; // return ""; >@Override public boolean equals(Object obj) < if(this == obj) return true; if(!(obj instanceof WordInfo)) return false; return this.plik.equals(((WordInfo) obj).plik); >@Override public int hashCode() < return this.plik.hashCode(); >> 

Unable to understand the relation between the code snippet and the question. Can you explain, where is the Set it in your code? Also, if your objects are exactly same, then why you want to replace?

@YogendraSingh — OP wants to be able to replace an old WordInfo in a set with a newer one that equals() the old one but is not the same object. (Note that the equals() test ignores the value of wystapienia .)

@TedHopp: Thanks, but still return this.plik.equals(((WordInfo) obj).plik); will make it to return true , no??

@YogendraSingh — My point was that two WordInfo objects can be equals() without being == . When two are equals() but not == , OP wants to be able to replace one with the other in a Set . OP’s question is: how to make the replace happen.

@TedHopp: I am trying to understand the question itself with the OP, i.e. why? , which I am still not convinced(may be this was pseudo example). I see your answer for how , which is good. I will leave it now. Thanks for your inputs.

5 Answers 5

Do a remove before each add:

 someSet.remove(myObject); someSet.add(myObject); 

The remove will remove any object that is equal to myObject. Alternatively, you can check the add result:

Which would be more efficient depends on how often you have collisions. If they are rare, the second form will usually do only one operation, but when there is a collision it does three. The first form always does two.

This causes me Exception in thread «main» java.util.ConcurrentModificationException when used with an Iterator

@ThreaT That is a separate problem from this question, which was about changing the behavior of add in a situation in which it could be called. I suggest asking it as a new question, with more background than you can put in a comment, unless you can find existing questions that help you.

The if statement from the second block of code could be simplified to if(someSet.remove(myObject)). as the remove method also returns true if myObject exists in the set. I would also think contains would be more efficient for the if, but less efficient on a whole when running the contents of the if statement.

If the set already contains an element that equals() the element you are trying to add, the new element won’t be added and won’t replace the existing element. To guarantee that the new element is added, simply remove it from the set first:

set.remove(aWordInfo); set.add(aWordInfo); 

I was working on a problem where I had a set then I wanted to replace/override some of the objects with objects from another set.

In my case what I ended up doing was creating a new set and putting the overrides in first then adding the current objects second. This works because a set won’t replace any existing objects when adding new objects.

Set currentInfo; Set overrides; 
for each override, replace the object in current info 
Set updated = new HashSet<>(); updated.addAll(overrides); updated.addAll(currentInfo); 

Try something as follows (this will only make sense if the equals and hashCode depends on one field, but the other fields could have different values):

Check out the documentation for the Set.add method

If this set already contains the element, the call leaves the set unchanged and returns false.

ok no -1, but now your answer is just the same as Patricia’s, and she was first to answer «correctly»

Check the HashSet code within the JDK. When an element is added and is a duplicate, the old value is replaced. Folk think that the new element is discarded, it’s wrong. So, you need no additional code in your case.

I re-read the code in JDK, and admit a mistake that I’ve made.

When put is made, the VALUE is replaced not the KEY from an HashMap .

Why am I talking about Hashmap . Because if you look at the HashSet code, you will notice:

So the PRESENT value is replaced with the new one as shown in this portion of code:

 public V put(K key, V value) < if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entrye = table[i]; e != null; e = e.next) < Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) < V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; >> modCount++; addEntry(hash, key, value, i); return null; > 

But I agree, the key isn’t replaced, and since the key s represent the HashSet’s values, this one is said to be «untouched».

Источник

Оцените статью