I'm having problems with Iterator.remove() called on a HashSet.
I've a Set of time stamped objects. Before adding a new item to the Set, I loop through the set, identify an old version of that data object and remove it (before adding the new object). the timestamp is included in hashCode and equals(), but not equalsData().
for (Iterator<DataResult> i = allResults.iterator(); i.hasNext();)
{
DataResult oldData = i.next();
if (data.equalsData(oldData))
{
i.remove();
break;
}
}
allResults.add(data)
The odd thing is that i.remove() silently fails (no exception) for some of the items in the set. I've verified
The line i.remove() is actually called. I can call it from the debugger directly at the breakpoint in Eclipse and it still fails to change the state of Set
DataResult is an immutable object so it can't have changed after being added to the set originally.
The equals and hashCode() methods use #Override to ensure they are the correct methods. Unit tests verify these work.
This also fails if I just use a for statement and Set.remove instead. (e.g. loop through the items, find the item in the list, then call Set.remove(oldData) after the loop).
I've tested in JDK 5 and JDK 6.
I thought I must be missing something basic, but after spending some significant time on this my colleague and I are stumped. Any suggestions for things to check?
EDIT:
There have been questions - is DataResult truly immutable. Yes. There are no setters. And when the Date object is retrieved (which is a mutable object), it is done by creating a copy.
public Date getEntryTime()
{
return DateUtil.copyDate(entryTime);
}
public static Date copyDate(Date date)
{
return (date == null) ? null : new Date(date.getTime());
}
FURTHER EDIT (some time later):
For the record -- DataResult was not immutable! It referenced an object which had a hashcode which changed when persisted to the database (bad practice, I know). It turned out that if a DataResult was created with a transient subobject, and the subobject was persisted, the DataResult hashcode was changed.
Very subtle -- I looked at this many times and didn't notice the lack of immutability.
I was very curious about this one still, and wrote the following test:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
public class HashCodeTest {
private int hashCode = 0;
#Override public int hashCode() {
return hashCode ++;
}
public static void main(String[] args) {
Set<HashCodeTest> set = new HashSet<HashCodeTest>();
set.add(new HashCodeTest());
System.out.println(set.size());
for (Iterator<HashCodeTest> iter = set.iterator();
iter.hasNext();) {
iter.next();
iter.remove();
}
System.out.println(set.size());
}
}
which results in:
1
1
If the hashCode() value of an object has changed since it was added to the HashSet, it seems to render the object unremovable.
I'm not sure if that's the problem you're running into, but it's something to look into if you decide to re-visit this.
Under the covers, HashSet uses HashMap, which calls HashMap.removeEntryForKey(Object) when either HashSet.remove(Object) or Iterator.remove() is called. This method uses both hashCode() and equals() to validate that it is removing the proper object from the collection.
If both Iterator.remove() and HashSet.remove(Object) are not working, then something is definitely wrong with your equals() or hashCode() methods. Posting the code for these would be helpful in diagnosis of your issue.
Are you absolutely certain that DataResult is immutable? What is the type of the timestamp? If it's a java.util.Date are you making copies of it when you're initializing the DataResult? Keep in mind that java.util.Date is mutable.
For instance:
Date timestamp = new Date();
DataResult d = new DataResult(timestamp);
System.out.println(d.getTimestamp());
timestamp.setTime(System.currentTimeMillis());
System.out.println(d.getTimestamp());
Would print two different times.
It would also help if you could post some source code.
You should all be careful of any Java Collection that fetches its children by hashcode, in the case that its child type's hashcode depends on its mutable state. An example:
HashSet<HashSet<?>> or HashSet<AbstaractSet<?>> or HashMap variant:
HashSet retrieves an item by its hashCode, but its item type
is a HashSet, and hashSet.hashCode depends on its item's state.
Code for that matter:
HashSet<HashSet<String>> coll = new HashSet<HashSet<String>>();
HashSet<String> set1 = new HashSet<String>();
set1.add("1");
coll.add(set1);
print(set1.hashCode()); //---> will output X
set1.add("2");
print(set1.hashCode()); //---> will output Y
coll.remove(set1) // WILL FAIL TO REMOVE (SILENTLY)
Reason being is HashSet's remove method uses HashMap and it identifies keys by hashCode, while AbstractSet's hashCode is dynamic and depends upon the mutable properties of itself.
Thanks for all the help. I suspect the problem must be with equals() and hashCode() as suggested by spencerk. I did check those in my debugger and with unit tests, but I've got to be missing something.
I ended up doing a workaround-- copying all the items except one to a new Set. For kicks, I used Apache Commons CollectionUtils.
Set<DataResult> tempResults = new HashSet<DataResult>();
CollectionUtils.select(allResults,
new Predicate()
{
public boolean evaluate(Object oldData)
{
return !data.equalsData((DataResult) oldData);
}
}
, tempResults);
allResults = tempResults;
I'm going to stop here-- too much work to simplify down to a simple test case. But the help is miuch appreciated.
It's almost certainly the case the hashcodes don't match for the old and new data that are "equals()". I've run into this kind of thing before and you essentially end up spewing hashcodes for every object and the string representation and trying to figure out why the mismatch is happening.
If you're comparing items pre/post database, sometimes it loses the nanoseconds (depending on your DB column type) which can cause hashcodes to change.
Have you tried something like
boolean removed = allResults.remove(oldData)
if (!removed) // COMPLAIN BITTERLY!
In other words, remove the object from the Set and break the loop. That won't cause the Iterator to complain. I don't think this is a long term solution but would probably give you some information about the hashCode, equals and equalsData methods
The Java HashSet has an issue in "remove()" method. Check the link below. I switched to TreeSet and it works fine. But I need the O(1) time complexity.
https://bugs.openjdk.java.net/browse/JDK-8154740
If there are two entries with the same data, only one of them is replaced... have you accounted for that? And just in case, have you tried another collection data structure that doesn't use a hashcode, say a List?
I'm not up to speed on my Java, but I know that you can't remove an item from a collection when you are iterating over that collection in .NET, although .NET will throw an exception if it catches this. Could this be the problem?
Related
I want to remove duplicates from a list but what I am doing is not working:
List<Customer> listCustomer = new ArrayList<Customer>();
for (Customer customer: tmpListCustomer)
{
if (!listCustomer.contains(customer))
{
listCustomer.add(customer);
}
}
Assuming you want to keep the current order and don't want a Set, perhaps the easiest is:
List<Customer> depdupeCustomers =
new ArrayList<>(new LinkedHashSet<>(customers));
If you want to change the original list:
Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);
If the code in your question doesn't work, you probably have not implemented equals(Object) on the Customer class appropriately.
Presumably there is some key (let us call it customerId) that uniquely identifies a customer; e.g.
class Customer {
private String customerId;
...
An appropriate definition of equals(Object) would look like this:
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Customer)) {
return false;
}
Customer other = (Customer) obj;
return this.customerId.equals(other.customerId);
}
For completeness, you should also implement hashCode so that two Customer objects that are equal will return the same hash value. A matching hashCode for the above definition of equals would be:
public int hashCode() {
return customerId.hashCode();
}
It is also worth noting that this is not an efficient way to remove duplicates if the list is large. (For a list with N customers, you will need to perform N*(N-1)/2 comparisons in the worst case; i.e. when there are no duplicates.) For a more efficient solution you could use a HashSet to do the duplicate checking. Another option would be to use a LinkedHashSet as explained in Tom Hawtin's answer.
java 8 update
you can use stream of array as below:
Arrays.stream(yourArray).distinct()
.collect(Collectors.toList());
Does Customer implement the equals() contract?
If it doesn't implement equals() and hashCode(), then listCustomer.contains(customer) will check to see if the exact same instance already exists in the list (By instance I mean the exact same object--memory address, etc). If what you are looking for is to test whether or not the same Customer( perhaps it's the same customer if they have the same customer name, or customer number) is in the list already, then you would need to override equals() to ensure that it checks whether or not the relevant fields(e.g. customer names) match.
Note: Don't forget to override hashCode() if you are going to override equals()! Otherwise, you might get trouble with your HashMaps and other data structures. For a good coverage of why this is and what pitfalls to avoid, consider having a look at Josh Bloch's Effective Java chapters on equals() and hashCode() (The link only contains iformation about why you must implement hashCode() when you implement equals(), but there is good coverage about how to override equals() too).
By the way, is there an ordering restriction on your set? If there isn't, a slightly easier way to solve this problem is use a Set<Customer> like so:
Set<Customer> noDups = new HashSet<Customer>();
noDups.addAll(tmpListCustomer);
return new ArrayList<Customer>(noDups);
Which will nicely remove duplicates for you, since Sets don't allow duplicates. However, this will lose any ordering that was applied to tmpListCustomer, since HashSet has no explicit ordering (You can get around that by using a TreeSet, but that's not exactly related to your question). This can simplify your code a little bit.
List → Set → List (distinct)
Just add all your elements to a Set: it does not allow it's elements to be repeated. If you need a list afterwards, use new ArrayList(theSet) constructor afterwards (where theSet is your resulting set).
I suspect you might not have Customer.equals() implemented properly (or at all).
List.contains() uses equals() to verify whether any of its elements is identical to the object passed as parameter. However, the default implementation of equals tests for physical identity, not value identity. So if you have not overwritten it in Customer, it will return false for two distinct Customer objects having identical state.
Here are the nitty-gritty details of how to implement equals (and hashCode, which is its pair - you must practically always implement both if you need to implement either of them). Since you haven't shown us the Customer class, it is difficult to give more concrete advice.
As others have noted, you are better off using a Set rather than doing the job by hand, but even for that, you still need to implement those methods.
private void removeTheDuplicates(List<Customer>myList) {
for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) {
Customer customer = iterator.next();
if(Collections.frequency(myList, customer) > 1) {
iterator.remove();
}
}
System.out.println(myList.toString());
}
The "contains" method searched for whether the list contains an entry that returns true from Customer.equals(Object o). If you have not overridden equals(Object) in Customer or one of its parents then it will only search for an existing occurrence of the same object. It may be this was what you wanted, in which case your code should work. But if you were looking for not having two objects both representing the same customer, then you need to override equals(Object) to return true when that is the case.
It is also true that using one of the implementations of Set instead of List would give you duplicate removal automatically, and faster (for anything other than very small Lists). You will still need to provide code for equals.
You should also override hashCode() when you override equals().
Nearly all of the above answers are right but what I suggest is to use a Map or Set while creating the related list, not after to gain performance. Because converting a list to a Set or Map and then reconverting it to a List again is a trivial work.
Sample Code:
Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set
//prevents the adding order of the elements
for (String string: stringsList) {
stringsSet.add(string);
}
return new ArrayList<String>(stringsSet);
Two suggestions:
Use a HashSet instead of an ArrayList. This will speed up the contains() checks considerably if you have a long list
Make sure Customer.equals() and Customer.hashCode() are implemented properly, i.e. they should be based on the combined values of the underlying fields in the customer object.
As others have mentioned, you are probably not implementing equals() correctly.
However, you should also note that this code is considered quite inefficient, since the runtime could be the number of elements squared.
You might want to consider using a Set structure instead of a List instead, or building a Set first and then turning it into a list.
The cleanest way is:
List<XXX> lstConsultada = dao.findByPropertyList(YYY);
List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX));
and override hascode and equals over the Id's properties of each entity
IMHO best way how to do it these days:
Suppose you have a Collection "dups" and you want to create another Collection containing the same elements but with all duplicates eliminated. The following one-liner does the trick.
Collection<collectionType> noDups = new HashSet<collectionType>(dups);
It works by creating a Set which, by definition, cannot contain duplicates.
Based on oracle doc.
The correct answer for Java is use a Set. If you already have a List<Customer> and want to de duplicate it
Set<Customer> s = new HashSet<Customer>(listCustomer);
Otherise just use a Set implemenation HashSet, TreeSet directly and skip the List construction phase.
You will need to override hashCode() and equals() on your domain classes that are put in the Set as well to make sure that the behavior you want actually what you get. equals() can be as simple as comparing unique ids of the objects to as complex as comparing every field. hashCode() can be as simple as returning the hashCode() of the unique id' String representation or the hashCode().
Using java 8 stream api.
List<String> list = new ArrayList<>();
list.add("one");
list.add("one");
list.add("two");
System.out.println(list);
Collection<String> c = list.stream().collect(Collectors.toSet());
System.out.println(c);
Output:
Before values : [one, one, two]
After Values : [one, two]
I have an object in a LinkedHashSet that implements equals, hashCode and compareTo (in a superclass) but when I try to remove that exact object from the set set.remove(obj) the remove method returns false and the object remains in the set. Is the implementation of LinkedHashSet supposed to call the equals() method of its objects? Because it doesn't. Could this be a java bug? I'm running 1.6.0_25.
My guess would be that your object's hashCode() implementation is returning a different value than when you added the object to the set.
LinkedHashSet works fine for me:
import java.util.*;
public class Test {
public static void main( String[] args ) {
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
String s = "hi";
lhs.add( s );
System.out.println( lhs );
lhs.remove( s );
System.out.println( lhs );
}
}
Perhaps you're passing in a reference to a different object to the remove method? Are you sure you didn't change the reference in any way?
Also make sure that hashCode() returns the same value when you insert it as when you are trying to remove it.
The chances of this being a bug in LinkedHashSet are infinitessimnally small. You should dismiss this as a plausible explanation of your problem.
Assuming that this is a bug in your code, then it could be due to a number of things. For instance:
Your equals and hashCode methods are returning contradictory answers for the object.
Your equals or hashCode methods depend on mutable fields and those fields are being changed while the object is in the set. (For instance, if the hashcode value changes, the object is likely to be on the wrong hash chain, causing the remove method to not find it.)
You have declared the equals method as an overload, not an override of equals(Object). (That could explain why your equals is not being called ... assuming that your assertion is factually correct.)
The object you are trying to remove is (in reality) not the one you inserted.
Something else has already removed the object.
You are running a different version of some class that does not match the source code you have been examining.
Now, I know that you have dismissed some of these explanations. But that may have been premature. Review the evidence that you based that dismissal on.
Another approach you could use is to use a Java debugger to forensically examine the data structures (e.g. the innards of the LinkedHashSet) and single-step the code where the deletion is supposed to be happening.
I'm profiling some old java code and it appears that my caching of values using a static HashMap and a access method does not work.
Caching code (a bit abstracted):
static HashMap<Key, Value> cache = new HashMap<Key, Value>();
public static Value getValue(Key key){
System.out.println("cache size="+ cache.size());
if (cache.containsKey(key)) {
System.out.println("cache hit");
return cache.get(key);
} else {
System.out.println("no cache hit");
Value value = calcValue();
cache.put(key, value);
return value;
}
}
Profiling code:
for (int i = 0; i < 100; i++)
{
getValue(new Key());
}
Result output:
cache size=0
no cache hit
(..)
cache size=99
no cache hit
It looked like a standard error in Key's hashing code or equals code.
However:
new Key().hashcode == new Key().hashcode // TRUE
new Key().equals(new Key()) // TRUE
What's especially weird is that cache.put(key, value) just adds another value to the hashmap, instead of replacing the current one.
So, I don't really get what's going on here. Am I doing something wrong?
edit
Ok, I see that in the real code the Key gets used in other methods and changes, which therefore get's reflected in the hashCode of the object in the HashMap. Could that be the cause of this behaviour, that it goes missing?
On a proper #Override of equals/hashCode
I'm not convinced that you #Override (you are using the annotation, right?) hashCode/equals properly. If you didn't use #Override, you may have defined int hashcode(), or boolean equals(Key), neither of which would do what is required.
On key mutation
If you are mutating the keys of the map, then yes, trouble will ensue. From the documentation:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
Here's an example:
Map<List<Integer>,String> map =
new HashMap<List<Integer>,String>();
List<Integer> theOneKey = new ArrayList<Integer>();
map.put(theOneKey, "theOneValue");
System.out.println(map.containsKey(theOneKey)); // prints "true"
theOneKey.add(42);
System.out.println(map.containsKey(theOneKey)); // prints "false"
By the way, prefer interfaces to implementation classes in type declarations. Here's a quote from Effective Java 2nd Edition: Item 52: Refer objects by their interfaces
[...] you should favor the use of interfaces rather than classes to refer to objects. If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types.
In this case, if at all possible, you should declare cache as simply a Map instead of a HashMap.
I'd recommend double and triple checking the equals and hashCode methods. Note that it's hashCode, not hashcode.
Looking at the (abstracted) code, everything seems to be in order. It may be that the actual code is not like your redacted version, and that this is more a reflection of how you expect the code to work and not what is happening in practice!
If you can post the code, please do that. In the meantime, here are some pointers to try:
After adding a Key, use exactly the same Key instance again, and verify that it produces a cache hit.
In your test, verify the hashcodes are equal, and that the objects are equal.
Is the Map implementation really a HashMap? WeakHashMap will behave in the way you describe once the keys are no longer reachable.
I'm not sure what your Key class is, but (abstractly similarly to you) what I'd do for a simple check is:
Key k1 = new Key();
Key k2 = new Key();
System.out.println("k1 hash:" + k1.hashcode);
System.out.println("k2 hash:" + k2.hashcode);
System.out.println("ks equal:" + k1.equals(k2));
getValue(k1);
getValue(k2);
if this code shows the anomaly -- same hashcode, equal keys, yet no cache yet -- then there's cause to worry (or, better, debug your Key class;-). The way you're testing, with new Keys all the time, might produce keys that don't necessarily behave the same way.
I've been working all day and I somehow can't get this probably easy task figured out - probably a lack of coffee...
I have a synchronizedList where some Objects are being stored. Those objects have a field which is something like an ID. These objects carry information about a user and his current state (simplified).
The point is, that I only want one object for each user. So when the state of this user changes, I'd like to remove the "old" entry and store a new one in the List.
protected static class Objects{
...
long time;
Object ID;
...
}
...
if (Objects.contains(ID)) {
Objects.remove(ID);
Objects.add(newObject);
} else {
Objects.add(newObject);
}
Obviously this is not the way to go but should illustrate what I'm looking for...
Maybe the data structure is not the best for this purpose but any help is welcome!
EDIT:
Added some information...
A Set does not really seem to fit my purpose. The Objects store some other fields besides the ID which change all the time. The purpose is, that the list will somehow represent the latest activities of a user. I only need to track the last state and only keep that object which describes this situation.
I think I will try out re-arranging my code with a Map and see if that works...
You could use a HashMap (or LinkedHashMap/TreeMap if order is important) with a key of ID and a value of Objects. With generics that would be HashMap<Object, Objects>();
Then you can use
if (map.containsKey(ID)) {
map.remove(ID);
}
map.put(newID, newObject);
Alternatively, you could continue to use a List, but we can't just modify the collection while iterating, so instead we can use an iterator to remove the existing item, and then add the new item outside the loop (now that you're sure the old item is gone):
List<Objects> syncList = ...
for (Iterator<Objects> iterator = syncList.iterator(); iterator.hasNext();) {
Objects current = iterator.next();
if (current.getID().equals(ID)) {
iterator.remove();
}
}
syncList.add(newObject);
And you can't use a Set to have only the first one stored ?
because it basically is precisely what you require.
You could use a HashSet to store the objects and then override the hashCode method in the class that the HashSet will contain to return the hashcode of your identifying field.
A Map is easiest, but a Set reflects your logic better. In that case I'd advice a Set.
There are 2 ways to use a set, depending on the equals and hashCode of your data object.
If YourObject already uses the ID object to determine equals (and hashCode obeys the contract) you can use any Set you want, a HashSet is probably best then.
If YourObjects business logic requires a different equals, taking into account multiple fields beside the ID field, then a custom comparator should be used. A TreeSet is a Set which can use such a Comparator.
An example:
Comparator<MyObject> comp = new Comparator<MyObject>{
public int compare(MyObject o1, MyObject o2) {
// NOTE this compare is not very good as it obeys the contract but
// is not consistent with equals. compare() == 0 -> equals() != true here
// Better to use some more fields
return o1.getId().hashCode() < o2.getId().hashCode();
}
public boolean equals(Object other) {
return 01.getId().equals(o2.getId());
}
}
Set<MyObject> myObjects = new TreeSet(comp);
EDIT
I have updated the code above to reflect that id is not an int, as suggested by the question.
My first option would be a HashSet, this would require that you override the hashCode and equals methods (don't forget: if you override one, override consistently the other !) so that objects with the same ID field are considered equal.
But this might break something if this assumption is NOT to be made in other parts of your application. In that case you might opt for using a HashMap (with the ID as key) or implement your own MyHashSet class (backed by such a HashMap).
Let's say I have this type in my application:
public class A {
public int id;
public B b;
public boolean equals(Object another) { return this.id == ((A)another).id; }
public int hashCode() { return 31 * id; //nice prime number }
}
and a Set<A> structure. Now, I have an object of type A and want to do the following:
If my A is within the set, update its field b to match my object.
Else, add it to the 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.
Yuval =8-)
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...)
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.
You really want to use a Map<Integer,A>, not a Set<A>.
Then map the ID (even though it's also stored in A!) to the object. So storing new is this:
A a = ...;
Map<Integer,A> map = new HashMap<Integer,A>();
map.put( a.id, a );
Your complete update algorithm is:
public static void update( Map<Integer,A> map, A obj ) {
A existing = map.get( obj.id );
if ( existing == null )
map.put( obj.id, obj );
else
existing.b = obj.b;
}
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<Integer,B> is in fact what you want, then it collapses to nothing:
Map<Integer,B> map = new HashMap<Integer,B>();
// The insert-or-update is just this:
map.put( id, b );
I don't think you can make it any easier than using remove/add if you are using a Set.
set.remove(a);
set.add(a);
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).
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<A,A> 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 Map<Integer, A> map;
public ASet() {
map = new HashMap<Integer, A>();
}
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).
It's a bit outside scope, but you forgot to re-implement hashCode(). When you override equals please override hashCode(), even in an example.
For example; contains() will very probably go wrong when you have a HashSet implementation of Set as the HashSet uses the hashCode of Object to locate the bucket (a number which has nothing to do with business logic), and only equals() the elements within that bucket.
public class A {
public int id;
public B b;
public int hashCode() {return id;} // simple and efficient enough for small Sets
public boolean equals(Object another) {
if (object == null || ! (object instanceOf A) ) {
return false;
}
return this.id == ((A)another).id;
}
}
public class Logic {
/**
* Replace the element in data with the same id as element, or add element
* to data when the id of element is not yet used by any A in data.
*/
public void update(Set<A> data, A element) {
data.remove(element); // Safe even if the element is not in the Set
data.add(element);
}
}
EDIT Yuvalindicated correctly that Set.add does not overwrite an existing element, but only adds if the element is not yet in the collection (with "is" implemented by equals)