How to convert Java 8 map.remove to Java 1.6? - java

I have the following:
fruitMap.remove(fruitId, fruitProperties);
The fruitMap is:
private Map<FruitId, FruitProperties> fruitMap = new HashMap<FruitId, FruitProperties>();
When I attempt to build my code I get a:
ERROR
The method remove(Object) in the type Map<MyImplementation.FruitId, FruitProperties>
is not applicable for the arguments (Map<MyImplementation.FruitId, FruitProperties>)
What is the issue?
Note that thiis call is inside of a method "removeFruit()" inside my "FruitImplementation" class.

From the Javadocs:
The default implementation is equivalent to, for this map:
if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
map.remove(key);
return true;
} else
return false;
The default implementation makes no guarantees about synchronization or atomicity properties of this method. Any implementation providing atomicity guarantees must override this method and document its concurrency properties.
So you could use that default implementation. Put it in a static helper method maybe.
But if this is supposed to be thread-safe, you may need to add some synchronization code (or consider using a ConcurrentMap, which by the way already has the remove method since Java 5).

The remove(key, value) method removes the entry for key if it is currently mapped to value. The method was added in Java 1.8. The Javadoc for the Map interface mentions the following default implementation:
if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
map.put(key, newValue);
return true;
} else
return false;
Since the Objects class was only added in Java 1.7, for Java 1.6 you have to write the equality test yourself. So, if you don't need the return value of the method, you can replace map.remove(key, value) with:
if (map.containsKey(key) {
Object storedValue = map.get(key);
if (storedValue == null ? value == null : storedValue.equals(value)) {
map.remove(key);
}
}
Note that this is not thread-safe. If you access the map from multiple threads, you will have to add a synchronized block.

You'll have to test the value yourself:
if(fruitProperties.equals(fruitMap.get(fruitId)) {
fruitMap.remove(fruitId);
}
Note, my implementation here assumes you are testing a non-null fruitProperties object.

You need to do the following assuming your values cannot be null
if (fruitProperties.equals(fruitMap.get(fruitId))
fruitMap.remove(fruitId);
Note: for this to be thread safe you would need to wrap this in a synchronized block.

Here is complete solution, handling synchronization and specific cases like null values.
synchronized (fruitMap)
{
if ((fruitMap.containsKey(fruitId) // The key is present
&& (
(fruitProperties == null && fruitMap.get(fruitId) == null) // fruitProperties is null, so is the stored value
|| (fruitProperties != null && fruitProperties.equals(fruitMap.get(fruitId)))
)
)
{
fruitMap.remove(fruitId);
}
}
It works in Java 6, it's an equivalent to :
fruitMap.remove(fruitId, fruitProperties);

Objects.equals has an implementation like this :
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
Therefore, the default implementation of remove :
if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
map.remove(key);
return true;
} else
return false;
Can be written in Java 6 as :
if (map.containsKey(key) && ((map.get(key) == value) || (map.get(key) != null && map.get(key).equals(value)))) {
map.remove(key);
return true;
} else
return false;

As per the Java Doc, remove(Object key, Object value)
Removes the entry for the specified key only if it is currently mapped
to the specified value.
If your equals() is properly defined, you can do something like this
FruitProperties valueFromMap = map.get(key);
if(valueFromMap != null){
if( valueFromMap == originalValue || valueFromMap.equals(originalValue)){
map.remove(key);
}
}
Now that you're using simple HashMap, I assume that you'll take care of thread-safety by either synchronizing or change it to ConcurrentHashMap :)

Related

Revert Hashmap to previous value

I have a class called Varfoo that stores variables. I have another class called Replacement that uses a hashmap to replace the x into 2. With the forget method, it's meant to forget replacing x with 2.
Varfoo x = new VarFoo("x");
Replacement s = new Replacement();
s.put(new VarFoo("x"), new IntFoo(2));
x.applyReplacement(s);
s.forget(x);
Here's the forget method:
public boolean forget(VarFoo var) {
if (var == null) {
throw new NullPointerException();
} else {
if (replacementMap.containsKey(var)) {
replacementMap.remove(var);
return true;
} else {
return false;
}
}
}
It will result to null because I've removed the key itself, not what I intended. How do I revert it back to how it was?
Equals and hashcodes of Varfoo:
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof VarFoo))
return false;
if (o == this)
return true;
return name.equals(((VarFoo) o).name);
}
#Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
You can make a new implementation of Map, that contains two (e.g.) HashMaps. In normal mode it forwards every operation to map1. This is the original map. map2 is null.
When you make a savepoint, you assign an empty map to map2. get operations now go first to map2 and then, if not found, to map1. put operations go only to map2. When you call forget, you assign again null to map2.
Of course, you must implement all the other methods of the Map interface. But this should be a simple task. Take care of removes, if needed (maybe you will need a Set of removed keys.
Hint: You can use java.util.AbstractMap as a base for your implementation.

Java 8 compute() and computeIfPresent() to check an existing value

I have this piece of code :
if (notificationSend.get(key) != null && notificationSend.get(key).equals(value)) {
return true;
} else {
notificationSend.put(key, value);
return false;
}
and I want to know if it is possible to refactor it using Jav8 Enhancements like compute() , computeIfPresent() or computeIfAbsent()
Assuming value is non-null, you don't need to use a conditional, or any of those compute* methods.
ValueType oldValue = map.put(key, value);
return value.equals(oldValue);

Do I need client side locking while checking if a key exist or not in ConcurrentHashMap?

I know I could use the putIfAbsent of ConcurrentHashMap. However, I need to do a webservice call to fetch a value for the given key if it does not exist and then store it (kind of caching) so I don't need to the next time same key is being used. Which of the following is correct? I think second version is necessary with the synchronized.
Update 1: I cannot use any functional interface.
Update 2: Updating the code snippet based on the reply from Costi Ciudatu
private static final Map<String, String> KEY_VALUE_MAP = new ConcurrentHashMap<>();
public String getValueVersion1(String key) {
String value = KEY_VALUE_MAP.get(key);
if (value != null) {
return value;
}
// Else fetch the value for the key from the webservice.
value = doRestCall(key);
KEY_VALUE_MAP.putIfAbsent(key, value);
return value;
} // Version 1 Ends here.
public synchronized String getValueVersion2(String key) {
String value = KEY_VALUE_MAP.get(key);
if (value != null) {
return value;
}
// Else fetch the value for the key from the webservice.
value = doRestCall(key);
KEY_VALUE_MAP.put(key, value);
return value;
} // Version 2 ends here.
You should have a look at ConcurrentMap#computeIfAbsent which does that for you atomically:
return KEY_VALUE_MAP.computeIfAbsent(key, this::doRestCall);
Edit (to address your "no functional interface" constraints):
You only need "client side locking" if you want to make sure that you only invoke doRestCall once for any given key. Otherwise, this code would work just fine:
final String value = KEY_VALUE_MAP.get(key);
if (value == null) {
// multiple threads may call this in parallel
final String candidate = doRestCall(key);
// but only the first result will end up in the map
final String winner = KEY_VALUE_MAP.putIfAbsent(key, candidate);
// local computation result gets lost if another thread made it there first
// otherwise, our "candidate" is the "winner"
return winner != null ? winner : candidate;
}
return value;
However, if you do want to enforce that doRestCall is invoked only once for any given key (my guess is you don't really need this), you will need some sort of synchronization. But try to be a bit more creative than the "all-or-nothing" approach in your examples:
final String value = KEY_VALUE_MAP.get(key);
if (value != null) {
return value;
}
synchronized(KEY_VALUE_MAP) {
final String existing = KEY_VALUE_MAP.get(key);
if (existing != null) { // double-check
return existing;
}
final String result = doRestCall(key);
KEY_VALUE_MAP.put(key, result); // no need for putIfAbsent
return result;
}
If you want to use this second (paranoid) approach, you can also consider using the key itself for locking (to narrow the scope to the minimum). But this would probably require you to manage your own pool of keys, as syncrhonized (key.intern()) is not good practice.
This all relies on the fact that your doRestCall() method never returns null. Otherwise, you'll have to wrap the map values within an Optional (or some home-made pre-java8 alternative).
As a (final) side note, in your code samples you inverted the use of put() and putIfAbsent() (the latter is the one to use with no external synchronization) and you read the value twice for null-checking.

Set remove() function does not work

while working with my application I've encountered a problem while trying to remove object from the java collection ( Set pulled from database with EclipseLink ).
The object which I want to remove in an entity class which has overriden equals method.
I've even checked whether any of the objects in the collection is eqauls to the one I want to remove with the following code:
for(AlbumEntity entity : deleteGroup.getAlbums()){
System.out.println("VAL: " + deleteAlbum.equals(entity));
}
In this case, one of the values returned is true. However, if I do:
boolean result = deleteGroup.getAlbums().remove(deleteAlbum);
the value of result is false and the size of collection stays the same.
Thanks for your help in advance
edit:
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof AlbumEntity)) {
return false;
}
AlbumEntity other = (AlbumEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
A few possibilities:
1) There is a problem with the implementation of id's equals or hashCode methods. In this case, you could have id1.equals(id2) but id1.hashCode() != id2.hashCode(). This would cause inconsistency between equals and hashCode() for the album objects and could cause the symptoms you're seeing.
2) The id for one or more albums changes at some point after the for loop that checks deleteAlbum.equals(entity) for each album in the Set. If an id changes for an album, the remove() method may not be able to find it. An id could change from null to some non null number if got saved to the database - EclipseLink might do this for you without you explicitly asking it to.
3) Because of EclipseLink's meddling, deleteGroup might not actually be a HashSet when you run your code. The docs for EclipseLink suggest it will give you an "indirection object" instead of the java.util.Set (or java.util.HashSet I presume) declared in your class, depending on how it is configured. In that case, the contains and remove methods might not do what you expect them to.
See Overriding equals and hashCode in Java for more details on these and other possible problems involving equals and hashCode, which can cause bizarre behavior with Sets.
Okay let's try a bit of testing:
1:
Iterator<AlbumEntity> it = deleteGroup.getAlbums().iterator();
while(it.hasNext()){
AlbumEntity entity = it.next();
Assert.assertTrue(deleteGroup.getAlbums().contains(entity))
}
Does this test run successfully?

Best practice to validate null and empty collection in Java

I want to verify whether a collection is empty and null. Could anyone please let me know the best practice.
Currently, I am checking as below:
if (null == sampleMap || sampleMap.isEmpty()) {
// do something
}
else {
// do something else
}
If you use the Apache Commons Collections library in your project, you may use the CollectionUtils.isEmpty(...) and MapUtils.isEmpty(...) methods which respectively check if a collection or a map is empty or null (i.e. they are "null-safe").
The code behind these methods is more or less what user #icza has written in his answer.
Regardless of what you do, remember that the less code you write, the less code you need to test as the complexity of your code decreases.
That is the best way to check it. You could write a helper method to do it:
public static boolean isNullOrEmpty( final Collection< ? > c ) {
return c == null || c.isEmpty();
}
public static boolean isNullOrEmpty( final Map< ?, ? > m ) {
return m == null || m.isEmpty();
}
If you use Spring frameworks, then you can use CollectionUtils to check against both Collections (List, Array) and Map etc.
if(CollectionUtils.isEmpty(...)) {...}
When you use spring then you can use
boolean isNullOrEmpty = org.springframework.util.ObjectUtils.isEmpty(obj);
where obj is any [map,collection,array,aything...]
otherwise: the code is:
public static boolean isEmpty(Object[] array) {
return (array == null || array.length == 0);
}
public static boolean isEmpty(Object obj) {
if (obj == null) {
return true;
}
if (obj.getClass().isArray()) {
return Array.getLength(obj) == 0;
}
if (obj instanceof CharSequence) {
return ((CharSequence) obj).length() == 0;
}
if (obj instanceof Collection) {
return ((Collection) obj).isEmpty();
}
if (obj instanceof Map) {
return ((Map) obj).isEmpty();
}
// else
return false;
}
for String best is:
boolean isNullOrEmpty = (str==null || str.trim().isEmpty());
Personally, I prefer to use empty collections instead of null and have the algorithms work in a way that for the algorithm it does not matter if the collection is empty or not.
We'll check a Collection object is empty, null or not. these all methods which are given below, are present in org.apache.commons.collections4.CollectionUtils package.
Check on List or set type of collection Objects.
CollectionUtils.isEmpty(listObject);
CollectionUtils.isNotEmpty(listObject);
Check on Map type of Objects.
MapUtils.isEmpty(mapObject);
MapUtils.isNotEmpty(mapObject);
The return type of all methods is boolean.
You can use org.apache.commons.lang.Validate's "notEmpty" method:
Validate.notEmpty(myCollection) -> Validate that the specified argument collection is neither null nor a size of zero (no elements); otherwise throwing an exception.
If you need to check for null, that is the way. However, if you have control on this, just return empty collection, whenever you can, and check only for empty later on.
This thread is about the same thing with C#, but the principles applies equally well to java. Like mentioned there, null should be returned only if
null might mean something more specific;
your API (contract) might force you to return null.
For all the collections including map use: isEmpty method which is there on these collection objects. But you have to do a null check before:
Map<String, String> map;
........
if(map!=null && !map.isEmpty())
......

Categories