Scala: Check for null for List - java

I am new to Scala after coding for 10 years in Java. Still getting hold of functional programming.
How can I check if the list is null or not?
Code looks something like this:
val filterList = filters.map { filter =>
//some operations
}
//Other function
filterList.foldLeft(true)((result1, result2) => {
Now if filters is null then filterList is going to be null too.

If filters is null (which is different from being empty) then that indicates some pretty careless programing, but it can be handled.
val filterList = Option(filters).map(_.map { ...
Now filterList is of type Option[X] where X is the collection type for filters. Note the 1st map is to unwrap the Option and the 2nd map maps over the collection, except if filters was null, then the 2nd map is never invoked and the whole result is None.

val filterList = if(filters == null) Seq.empty[SomeType] else filters.map {...}
However, you should try to make sure it never gets to be null, because we try to avoid null variables in Scala. Use the Option[T] type, or empty collections instead

Related

Java Stream collected object in List, shows size as 1 but object is null

I am working on a SpringBoot project, where while testing I was deliberately passing something like this in the request and got a NullPointerException:
{
"materials": [
{
}
]
}
I don't want to use #NotNull on materials in the Request Class as the materials field can be null.
but when it is not null I want to make sure a few fields are present as based on that I need to validate its value.
final List<Material> materials = materialRequests.getMaterials().stream().map(MaterialRequest::getMaterial).collect(Collectors.toList());
// Throws NullPointerException here
final Set<String> materialIds = materials.stream().map(Material::getUuid).collect(Collectors.toSet());
While debugging I saw this:
materials shows it has 1 element however it is null, and due to that, I get a null pointer expectation.
How can I put a check here to see whether it is null or not.. and why is the behavior like this?
Filter out null elements from the stream before applying map():
final Set<String> materialIds = materials.stream()
.filter(Objects::nonNull)
.map(Material::getUuid)
.collect(Collectors.toSet());
use .filter() to filter out null values:
final Set<String> materialIds = materials.stream().filter(s -> s != null).map(Material::getUuid).collect(Collectors.toSet());

Layered filtering using Java Stream API

I have some imperative Java conditional code that I want to refactor to use Streams.
Specifically, I have this map that I want to filter into a List based on specific filter criteria.
private Map<Integer,Thing> thingMap = new HashMap<Integer,Thing>();
// populate thingMap
And here's the code that uses it:
List<Thing> things = new ArrayList<Thing>();
for (Thing thing : thingMap.values()) {
if (thing.getCategory().equals(category)) {
if (location == null) {
things.add(thing);
} else if (thing.getLocation().equals(location)) {
things.add(thing);
}
}
}
I refactored that to the following. But what's missing is I want the location to be checked only if the category filter passes. Also, I suspect there's a better way to do this:
List<Thing> things = thingMap.entrySet()
.stream()
.filter(t -> t.getValue().getCategory().equals(category))
.filter(t ->
location == null ||
t.getValue().getLocation().equals(location)
)
.map(Map.Entry::getValue)
.collect(Collectors.toList());
What would be the idiomatic approach to retaining the layered conditional checks using Streams?
Operations chained after a filter will only be executed for elements accepted by the predicate. So there is no need to worry about that.
You could also join the conditions into a single filter step, just like you could join the nested if statements into a single if, by combining the conditions using &&. The result is the same.
But note that the loop uses the condition location == null, referring to the variable declared outside the code snippet you have posted, not thing.getLocation() == null.
Besides that, you made other unnecessary changes compared to the loop. The loop iterates over the values() view of the map whereas you used entrySet() for the Stream instead, introducing the need to call getValue() on a Map.Entry four times.
A straight-forward translation of the loop logic is much simpler:
List<Thing> things = thingMap.values().stream()
.filter(thing -> thing.getCategory().equals(category))
.filter(thing -> location == null || thing.getLocation().equals(location))
.collect(Collectors.toList());

Aggregate values and convert into single type within the same Java stream

I have a class with a collection of Seed elements. One of the method's return type of Seed is Optional<Pair<Boolean, String>>.
I'm trying to loop over all seeds, find if any boolean value is true and at the same time, create a set with all the String values. For instance, my input is in the form Optional<Pair<Boolean, String>>, the output should be Optional<Signal> where Signal is like:
class Signal {
public boolean exposure;
public Set<String> alarms;
// constructor and getters (can add anything to this class, it's just a bag)
}
This is what I currently have that works:
// Seed::hadExposure yields Optional<Pair<Boolean, String>> where Pair have key/value or left/right
public Optional<Signal> withExposure() {
if (seeds.stream().map(Seed::hadExposure).flatMap(Optional::stream).findAny().isEmpty()) {
return Optional.empty();
}
final var exposure = seeds.stream()
.map(Seed::hadExposure)
.flatMap(Optional::stream)
.anyMatch(Pair::getLeft);
final var alarms = seeds.stream()
.map(Seed::hadExposure)
.flatMap(Optional::stream)
.map(Pair::getRight)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
return Optional.of(new Signal(exposure, alarms));
}
Now I have time to make it better because Seed::hadExposure could become and expensive call, so I was trying to see if I could make all of this with only one pass. I've tried (some suggestions from previous questions) with reduce, using collectors (Collectors.collectingAndThen, Collectors.partitioningBy, etc.), but nothing so far.
It's possible to do this in a single stream() expression using map to convert the non-empty exposure to a Signal and then a reduce to combine the signals:
Signal signal = exposures.stream()
.map(exposure ->
new Signal(
exposure.getLeft(),
exposure.getRight() == null
? Collections.emptySet()
: Collections.singleton(exposure.getRight())))
.reduce(
new Signal(false, new HashSet<>()),
(leftSig, rightSig) -> {
HashSet<String> alarms = new HashSet<>();
alarms.addAll(leftSig.alarms);
alarms.addAll(rightSig.alarms);
return new Signal(
leftSig.exposure || rightSig.exposure, alarms);
});
However, if you have a lot of alarms it would be expensive because it creates a new Set and adds the new alarms to the accumulated alarms for each exposure in the input.
In a language that was designed from the ground-up to support functional programming, like Scala or Haskell, you'd have a Set data type that would let you efficiently create a new set that's identical to an existing set but with an added element, so there'd be no efficiency worries:
filteredSeeds.foldLeft((false, Set[String]())) { (result, exposure) =>
(result._1 || exposure.getLeft, result._2 + exposure.getRight)
}
But Java doesn't come with anything like that out of the box.
You could create just a single Set for the result and mutate it in your stream's reduce expression, but some would regard that as poor style because you'd be mixing a functional paradigm (map/reduce over a stream) with a procedural one (mutating a set).
Personally, in Java, I'd just ditch the functional approach and use a for loop in this case. It'll be less code, more efficient, and IMO clearer.
If you have enough space to store an intermediate result, you could do something like:
List<Pair<Boolean, String>> exposures =
seeds.stream()
.map(Seed::hadExposure)
.flatMap(Optional::stream)
.collect(Collectors.toList());
Then you'd only be calling the expensive Seed::hadExposure method once per item in the input list.

What are the java collections accepts adding null values?

I know Set and Map accepts null values and today I just found out LinkedList also accepts null values like
map.put(null, 1);
set.add(null);
linkedList.add(null)
Is there any other collections that allow null values to be stored?. Just posting to get a comprehensive list in one place and reason for each for them.
Set and Map are interfaces. They have several implementations in Java.
Popular implementations of the Map are:
HashMap - accepts one null key
Hashtable - doesn't accept any null key
TreeMap - doesn't accept any null key
LinkedHashMap - accepts one null key
Any number of null values can be added as value in any of above implementations
Popular implementations of the Set are:
HashSet - accepts one null element
TreeSet - doesn't accept any null element
LinkedHashSet - accepts one null element
Any implementations of List, like ArrayList or LinkedList can accept nulls.
This problem can be resolved by using placeholder class Optional from JDK.
The principle is to package a nullable value within an wrapper instance using Optional class that will carry values (null values also).
It can be annoying and little heavy in the code but surely can be suitable in many cases.
Here is an example with a non-null value :
Optional<Integer> valuedInt = Optional.ofNullable(123);
assertTrue(valuedInt.isPresent());
assertEquals(Integer.valueOf(123), valuedInt.get());
Here is an example with a null value :
Optional<Integer> nullInt = Optional.ofNullable(null);
assertTrue(nullInt.isEmpty());
try {
var val = nullInt.get();
} catch (Exception e) {
// Invocation of nullInt.get() throws an exception.
assertTrue(e instanceof NoSuchElementException);
}
// However, the method Optional.orElse(T) can be used as
// a getter that supplies the value wether it is valued or null.
assertEquals(Integer.valueOf(123), valuedInt.orElse(null));
assertEquals(null, nullInt.orElse(null));
We can initialize a list as so :
// Our hacked list
List<Optional<Integer>> integersOpt;
First way to populate the list :
integersOpt = new ArrayList<>();
integersOpt.add(Optional.ofNullable(1));
integersOpt.add(Optional.ofNullable(null));
integersOpt.add(Optional.ofNullable(2));
integersOpt.add(Optional.ofNullable(null));
integersOpt.add(Optional.ofNullable(3));
Second way to populate the list (unmodifiable) :
integersOpt =
Arrays.asList(1, null, 2, null, 3).stream()
.map(x -> Optional.ofNullable(x))
.collect(Collectors.toList());
Third way to populate the list (modifiable) :
integersOpt =
new ArrayList<>(
Arrays.asList(1, null, 2, null, 3).stream()
.map(x -> Optional.ofNullable(x))
.collect(Collectors.toList())
);
Count and print values :
int countNulls = 0, countNonNulls = 0;
for(Optional<Integer> opt : integersOpt) {
Integer optVal = opt.orElse(null); // Our famous "getter" function.
if(optVal == null) {countNulls++;}
else {countNonNulls++;}
System.out.println("Original value = " + optVal);
}
assertEquals(2, countNulls);
assertEquals(3, countNonNulls);
integersOpt.toString() will return this value :
[Optional[1], Optional.empty, Optional[2], Optional.empty, Optional[3]]
Accepting nulls was a design mistake, as time has proven. First of all this complicates implementations, because at some point in time you might need to be able to compare null to null, for example; and this sometimes require special handling (or special branches in the code at least).
Since java-9, immutable collections all reject null and even document it properly. One minor example (but there are many more) is Set:of, that says:
#throws NullPointerException if the element is null
Not only that, but they also don't allow null checks, so you might be surprised by this a bit:
Set<Object> oldS = new HashSet<>();
oldS.add(new Object());
System.out.println(oldS.contains(null)); // false
Set<Object> newS = Set.of(new Object());
System.out.println(newS.contains(null)); // NPE
Every new collection or implementation that comes to JDK prohibits null.

What is inside an empty index of a Hashmap?

I have a hashmap that is 101 keys in size, but I know for sure about 6 of those have no data inside, and there may be more without data as well. What exactly is inside the empty indexes? Is it null? or is there a Hash(index).isEmpty() method that I can use to see if its empty?
I realize there is a isEmpty method inside hashmap, but I thought that only checked if the entire map was empty not just a single index.
I realize there is a isEmpty method
inside hashmap, but I thought that
only checked if the entire map was
empty not just a single index.
I think what you're looking for is the containsKey(Object) method. According to the documentation:
Returns true if this map contains a
mapping for the specified key. More
formally, returns true if and only if
this map contains a mapping for a key
k such that (key==null ? k==null :
key.equals(k)). (There can be at most
one such mapping.)
Parameters:
key - key whose presence in this map is to be tested
Returns:
true if this map contains a mapping for the specified key
Well, for the keys to arrive there with no data, you have to put them there.
If you did map.put(key, null) then yes the data for that key is null. You always have to give the second parameter to the method, you can't just map.put(key).
If you know for sure that a certain key should have no data you could try going into debug mode and putting a watch for myMap.get(myEmptyKey) and see what you get (in case that no data is an empty object or something else, you should be able to see that).
Edit: Some code would be useful to help you, but if I understand correctly you do something like this:
for (Object obj : list) {
if (matchesCriteriaX(obj)) {
map.put("X", obj);
else if (matchesCriteriaY(obj)) {
map.put("Y", obj);
}
}
Well, if you do that and try to do map.get("X"), but you never actually put anything for that key (becaus no object matched criteria X), you will most definitely get back a null.
On the other hand, if you did something like
Map<String, List<Object>> map = new HashMap<String, List<Object>>();
map.add("X", new ArrayList<Object>());
map.add("Y", new ArrayList<Object>());
for (Object obj : list) {
if (matchesCriteriaX(obj)) {
List<Object> list = map.get("X");
list.add(obj);
else if (matchesCriteriaY(obj)) {
List<Object> list = map.get("Y");
list.add(obj);
}
}
then you could check if a category is empty by doing map.get("x").isEmpty() since List has that method (and it would be empty if no object matched the key criteria).
Judging from what you said, I'm suspecting something like this:
Map<SomeKey, List<SomeValue>> yourMap;
If this is the case, what you can do is
if( yourMap.contains(someKey) ){
List<SomeValue> someList = yourMap.get(someKey);
if(someList.size() == 0){
// it's empty, do something?
}
}

Categories