null-safe mapping Comparator using default implementations
Found a possible solution:
Comparator.comparing(ToSort::getSortBy,
Comparator.nullsFirst(Comparator.naturalOrder())
)
Java8 Null-safe comparison
It should be
Comparator<IShopProduct> comparator =
Comparator.comparing( IShopProduct::getVintage,
Comparator.nullLast(naturalOrder()));
Comparator.nullFirst()/nullLast()
consider null value being greater/smaller than nonNull object
Edit
This is implementation of Comparator.comparing():
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
As you can see it calls keyExtractor.apply(c1).compareTo()
so it will throw NPE if keyExtractor.apply(c1)
is null
My suggested code using the following function:
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
Basically it will extracts the value, then pass the comparing values to Comparator
.
The values will be passed to the naturalOrder()
comparator which resolved to value1.compareTo(value2)
. Normally it will throw NPE but we have wrapped it with Comparator.nullLast
which have special null
handler.
Comparator.nullsFirst with null-safe-comparator
nullsFirst
will take care of null Item
s ("i1
"), but once two valid objects are found, your Comparator
is invoked and you need to handle internal null
references.
In your case, you could use something like:
items.sort(
Comparator.nullsFirst(
Comparator.comparing(Item::getUser,
Comparator.nullsFirst(Comparator.comparingDouble(User::getValue))
)
)
);
(Assuming getValue()
returns a double
)
But I hardly recommend such convoluted code.
Null safe date comparator for sorting in Java 8 Stream
If it's the Item
s that may be null, use @rgettman's solution.
If it's the LocalDate
s that may be null, use this:
items.stream()
.sorted(Comparator.comparing(Item::getCreateDt, Comparator.nullsLast(Comparator.reverseOrder())));
In either case, note that sorted().findFirst()
is likely to be inefficient as most standard implementations sort the entire stream first. You should use Stream.min instead.
Java 8 lambda comparator with null value check
There are default implementations within Comparator you can use: nullsFirst
or nullsLast
:
Comparator.comparing(YourObject::getStartDate,
Comparator.nullsFirst(Comparator.naturalOrder())
)
Null safe comparator with two compares in java
If I understood correctly, you want something like this:
roomTOs.sort(Comparator
.comparing(
personTO::getPersonName)
.thenComparing(
personTO::getUsedDate, Comparator.nullsLast(Comparator.naturalOrder()))
);
How to simplify creation of null-safe Comparator on multiple fields?
It sounds like your Action
class has five fields, and you want to create a composite Comparator over all the fields, and that's also null-safe. You could do this:
Comparator<Action> COMPARATOR1 =
comparing(Action::date, nullsLast(naturalOrder()))
.thenComparing(Action::time, nullsLast(naturalOrder()))
.thenComparing(Action::foo, nullsLast(naturalOrder()))
.thenComparing(Action::bar, nullsLast(naturalOrder()))
.thenComparing(Action::baz, nullsLast(naturalOrder()));
If repeating nullsLast(naturalOrder())
is frustrating, then one can create some utilities to help. First, we observe that naturalOrder()
returns a singleton Comparator instance. It works for any Comparable type; all it does is call the compareTo()
method.
The nullsLast()
wrapper simply adds null checking before delegating to its wrapped Comparator. It doesn't depend at all on the actual type it's comparing. It's not a singleton, but any nulls-last-natural-order comparator is as good as any other. Thus we can create a single instance and reuse it. We wrap it in a method in order to avoid having to cast it to the right type at every use site.
static final Comparator<?> NLNO = Comparator.nullsLast(Comparator.naturalOrder());
@SuppressWarnings("unchecked")
static <T extends Comparable<T>> Comparator<T> nlno() {
return (Comparator<T>)NLNO;
}
This lets us do the following instead:
Comparator<Action> COMPARATOR2 =
comparing(Action::date, nlno())
.thenComparing(Action::time, nlno())
.thenComparing(Action::foo, nlno())
.thenComparing(Action::bar, nlno())
.thenComparing(Action::baz, nlno());
Still too verbose? We can write a method that takes a variable number of accessor methods and creates a combined comparator based on them, by reducing over thenComposing()
. That looks like this:
@SafeVarargs
@SuppressWarnings("varargs")
static <C extends Comparable<C>> Comparator<Action>
comparatorWith(Function<Action, ? extends C>... extractors) {
return Arrays.stream(extractors)
.map(ex -> comparing(ex, nullsLast(naturalOrder())))
.reduce(Comparator::thenComparing)
.orElseThrow(() -> new IllegalArgumentException("need at least one extractor"));
}
This enables us to write the following:
Comparator<Action> COMPARATOR3 =
comparatorWith(Action::date, Action::time, Action::foo, Action::bar, Action::baz);
Is it worth it? The helper method is probably more complicated than writing out a single chain of nullsLast(naturalOrder())
calls. If you need a bunch of different comparator chains, though, then the ability to reuse the helper method might be worth its complexity.
Working of Comparator.nullsFirst when both are null
The comparator returned by nullsFirst(…)
returns a comparator which handles the case that one or both of the objects to compare are null
.
So when you say nullsFirst(comparing(MyEntity::getIsoCode))
, you get a comparator handling the case that either or both MyEntity
instances are null
and comparing the getIsoCode
property according to their natural order (not handling null
values), if neither MyEntity
instance is null
.
What you want achieve, is comparing(MyEntity::getIsoCode, nullsFirst(naturalOrder()))
, specify the null
-safe comparator to be used to compare the property values. The same applies to the getEndDate
property.
You may fuse it with thenComparing
, to previousComparator.thenComparing(MyEntity::getIsoCode, nullsFirst(naturalOrder()))
Java: rewrite comparator to Lambda - nullsafe
Your code will put null Domain
s first. If you want to check for null rank, you need to use this:
Comparator.comparing(Domain::getDomainRank, Comparator.nullsFirst(Comparator.naturalOrder()))
But keep in mind that this is only equivalent to your original comparator if the rank can't be less than 0. Otherwise, you'll have to test a similar expression:
Comparator.comparing(d -> d.getDomainRank() == null ? 0 : d.getDomainRank())
Alternatively, you might have meant to use Float.MIN_VALUE
instead of 0 in your original code.
Related Topics
Hibernate Table Not Mapped Error in Hql Query
What Is Xml Bom and How to Detect It
How to Copy Data from File to Postgresql Using Jdbc
Java Benchmarking - Why Is the Second Loop Faster
How to Serialize Static Data Members of a Java Class
Invokeandwait Method in Swingutilities
Java Static Initialization Order
Hibernate - Foreign Keys Instead of Entities
Java Enum Methods - Return Opposite Direction Enum
How to Make Hashmap Work with Arrays as Key
How to Deserialize a Blank JSON String Value to Null for Java.Lang.String
Lists with Wildcards Cause Generic Voodoo Error
How to Solve the "A Generic Array of T Is Created for a Varargs Parameter" Compiler Warning