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

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

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 optional map and execute different functions

What should be the Java 8 equivalent of
if ( indicator != null ){
return getTest(a,indicator);
else{
return getTest(a);
}
if indicator is to be an Optional instead of nullable?
You can write:
Java
Optional<Boolean> indicator = ...;
return indicator.map(i -> getTest(a, i)).orElseGet(() -> getTest(a));
But not always is Optional better than simple null.
Probably provoking answer but the Java 8 version of :
if (indicator != null){
return getTest(a,indicator);
}
else{
return getTest(a);
}
is the same thing :
if (indicator != null){
return getTest(a,indicator);
}
else{
return getTest(a);
}
And in fact I would probably use that more terse way :
if (indicator != null){
return getTest(a,indicator);
}
return getTest(a);
In your case, using Optional with "chaining" methods such as :
return indicator.map(i -> getTest(a, i)).orElseGet(() -> getTest(a));
hides the flow logic while your actual code shows that perfectly: you have two distinct cases. In a general way, you want to chain processings/transformations but you don't want to chain forking scenarios since these are different scenarios.
If you want to use Optional to convey the fact that this object may be null, which is perfectly legitimate, I would probably do something that mimics the if-else logic that is clear :
Optional<Indicator> optIndicator = findIndicator();
if (indicator.isPresent()){
return getTest(a,indicator.get());
}
return getTest(a);
Simply you can write:
No time complexity and space.
Optional<Boolean> indicator = ...;
if(indicator.isPresent()) {
// value is present inside Optional
return getTest(a,indicator);
} else {
// value is absent
return getTest(a);
}
You could write it simply:
boolean indicator = ...;
return indicator ? getTest(a, i) : getTest(a);
Or
Optional<Boolean> indicator = ...;
return indicator.orElse(false) ? getTest(a, i) : getTest(a);

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

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 :)

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())
......

Greater-than compare-and-swap

As the title suggests, I'm looking for a compare-and-swap implementation, but with greater-than comparison:
if(newValue > oldValue) {
oldValue = newValue;
}
where oldValue is some global shared state and newValue is private to each thread, without doing this:
synchronized(locker) {
if(newValue > oldValue) {
oldValue = newValue;
}
}
because I want a non-blocking solution. From studying source codes of other non-blocking operations, I've come up with this (assuming the values are integers):
AtomicInteger oldValue; // shared global variable
...
public boolean GreaterThanCAS(int newValue) {
while(true) {
int local = oldValue;
if(local == oldValue) {
if(newValue > local) {
if(oldValue.compareAndSet(local, newValue) {
return true; // swap successful
} // else keep looping
} else {
return false; // swap failed
}
} // else keep looping
}
}
when // else keep looping happens, it means that another thread has changed the oldValue in the meantime and so I need to loop and try again.
Is this implementation correct (thread-safe)?
Since Java 8 this can be simplified with use of updateAndGet:
public boolean greaterThanCAS(int newValue) {
return oldValue.updateAndGet(x -> x < newValue ? newValue : x) == newValue;
}
Note that this would return true also in case when old and new values are equal.
Give a try to #Adam's answer if this is not desired behaviour.
I see no problems with your implementation, provided that no thread ever decreases the value of the AtomicInteger. If they do, your code is open to race conditions.
Note that the code can be simplified as follows:
public boolean GreaterThanCAS(int newValue) {
while(true) {
int local = oldValue.get();
if(newValue <= local) {
return false; // swap failed
}
if(oldValue.compareAndSet(local, newValue)) {
return true; // swap successful
}
// keep trying
}
}
I would re write it to look more like:
while(true) {
int local = oldValue.get();
if(newValue > local){
if(oldValue.compareAndSwap(local, newValue) {
return true; // swap successful
} // else keep looping
}else
return false;
}
The equivalence check before the greater than check is redundant.
Otherwise it should work fine.
#Vadzim, I would have commented on your post, but stackoverflow says I don't have enough points to post comments. Your answer is almost correct, but your function will always return false because getAndUpdate always returns the previous value, or 'x' in your case. I think all you would need to do is replace your last '==' with '<', e.g.:
// return true if the assignment was made, false otherwise
public boolean greaterThanCAS(int newValue) {
return oldValue.getAndUpdate(x -> x < newValue ? newValue : x) < newValue;
}

Categories