Null-Safe Mapping Comparator Using Default Implementations

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 Items ("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 Items that may be null, use @rgettman's solution.

If it's the LocalDates 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 Domains 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



Leave a reply



Submit