Comparator:如何使用nullsFirst和stream api?
Comparator: How to use nullsFirst and stream api?
我正在努力编写一个 Comparator
来处理空字段。
我有这样的pojo:
import static java.util.Comparator.comparingInt;
import java.math.BigDecimal;
import java.util.Comparator;
import lombok.Builder;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
@Data
@Builder
public class Pojo implements Comparable<Pojo> {
private Integer id;
private BigDecimal cost;
private static final Comparator<Pojo> COMPARATOR =
comparingInt((Pojo p) -> p.id).thenComparing(p -> p.cost);
@Override
public int compareTo(@NotNull Pojo pojo) {
return COMPARATOR.compare(this, pojo);
}
}
还有一些测试:
import static org.assertj.core.api.Assertions.assertThat;
import java.math.BigDecimal;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class PojoShould {
private static final int SMALLER = -1;
private static final int LARGER = 1;
private static final int EQUAL = 0;
@ParameterizedTest
@MethodSource
void compareToWorks(Pojo p1, Pojo p2, int expectedResult) {
assertThat(p1.compareTo(p2)).isEqualTo(expectedResult);
}
@SuppressWarnings("unused")
static Stream<Arguments> compareToWorks() {
return Stream.of(
Arguments.of(Pojo.builder().id(-1).build(), Pojo.builder().id(0).build(), SMALLER),
Arguments.of(Pojo.builder().id(0).build(), Pojo.builder().id(-1).build(), LARGER),
Arguments.of(Pojo.builder().id(0).build(), Pojo.builder().id(0).build(), EQUAL),
Arguments.of(
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
Pojo.builder().id(0).cost(BigDecimal.ONE).build(),
SMALLER),
Arguments.of(
Pojo.builder().id(0).cost(BigDecimal.ONE).build(),
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
LARGER),
Arguments.of(
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
EQUAL));
}
}
大多数情况下都有效,但 id 相等的情况下,它会尝试比较 null
成本(当然)。
如何使 Comaparator null 安全?我试过了
private static final Comparator<Pojo> COMPARATOR =
nullsFirst(comparingInt((Pojo p) -> p.id))
.thenComparing(nullsFirst(comparing((p -> p.cost))));
但这于事无补。
这是 Comparator#thenComparing(Function,Comparator)
的文档:
Returns a lexicographic-order comparator with a function that extracts a key to be compared with the given Comparator
.
Implementation Requirements:
This default implementation behaves as if thenComparing(comparing(keyExtractor, cmp))
.
哪些引用 Comparator#comparing(Function,Comparator)
:
Accepts a function that extracts a sort key from a type T
, and returns a Comparator<T>
that compares by that sort key using the specified Comparator
.
The returned comparator is serializable if the specified function and comparator are both serializable.
如文档所述,Comparator
参数用于比较 Function
提取的值。这意味着 that Comparator
必须能够处理 null
。这与您似乎想要做的相反。
无需手动编写代码即可实现 null
安全 Comparator
的一种方法是使用 Comparator#nullsFirst(Comparator)
or Comparator#nullsLast(Comparator)
。这两个方法都包装了另一个 Comparator
,如果要比较的两个元素都不是 null
,将被调用。基本上,nullsFirst
或 nullsLast
返回的 Comparator
是一个拦截器。
现在只需要 Comparator
将被 null
-safe Comparator
包装。由于Integer
和BigDecimal
都是Comparable
,我们可以使用Comparator#naturalOrder()
.
假设 id
和 cost
都可以是 null
,并且您希望 null
值在每种情况下都排在第一位,那么以下内容应该适合您:
comparing(p -> p.id, nullsFirst(naturalOrder()))
.thenComparing(p -> p.cost, nullsFirst(naturalOrder()));
或者,如果您假设 id
永远不会是 null
,您可以像这样修改上面的内容:
comparing(p -> p.id)
.thenComparing(p -> p.cost, nullsFirst(naturalOrder()));
我正在努力编写一个 Comparator
来处理空字段。
我有这样的pojo:
import static java.util.Comparator.comparingInt;
import java.math.BigDecimal;
import java.util.Comparator;
import lombok.Builder;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
@Data
@Builder
public class Pojo implements Comparable<Pojo> {
private Integer id;
private BigDecimal cost;
private static final Comparator<Pojo> COMPARATOR =
comparingInt((Pojo p) -> p.id).thenComparing(p -> p.cost);
@Override
public int compareTo(@NotNull Pojo pojo) {
return COMPARATOR.compare(this, pojo);
}
}
还有一些测试:
import static org.assertj.core.api.Assertions.assertThat;
import java.math.BigDecimal;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class PojoShould {
private static final int SMALLER = -1;
private static final int LARGER = 1;
private static final int EQUAL = 0;
@ParameterizedTest
@MethodSource
void compareToWorks(Pojo p1, Pojo p2, int expectedResult) {
assertThat(p1.compareTo(p2)).isEqualTo(expectedResult);
}
@SuppressWarnings("unused")
static Stream<Arguments> compareToWorks() {
return Stream.of(
Arguments.of(Pojo.builder().id(-1).build(), Pojo.builder().id(0).build(), SMALLER),
Arguments.of(Pojo.builder().id(0).build(), Pojo.builder().id(-1).build(), LARGER),
Arguments.of(Pojo.builder().id(0).build(), Pojo.builder().id(0).build(), EQUAL),
Arguments.of(
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
Pojo.builder().id(0).cost(BigDecimal.ONE).build(),
SMALLER),
Arguments.of(
Pojo.builder().id(0).cost(BigDecimal.ONE).build(),
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
LARGER),
Arguments.of(
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
Pojo.builder().id(0).cost(BigDecimal.ZERO).build(),
EQUAL));
}
}
大多数情况下都有效,但 id 相等的情况下,它会尝试比较 null
成本(当然)。
如何使 Comaparator null 安全?我试过了
private static final Comparator<Pojo> COMPARATOR =
nullsFirst(comparingInt((Pojo p) -> p.id))
.thenComparing(nullsFirst(comparing((p -> p.cost))));
但这于事无补。
这是 Comparator#thenComparing(Function,Comparator)
的文档:
Returns a lexicographic-order comparator with a function that extracts a key to be compared with the given
Comparator
.Implementation Requirements:
This default implementation behaves as if
thenComparing(comparing(keyExtractor, cmp))
.
哪些引用 Comparator#comparing(Function,Comparator)
:
Accepts a function that extracts a sort key from a type
T
, and returns aComparator<T>
that compares by that sort key using the specifiedComparator
.The returned comparator is serializable if the specified function and comparator are both serializable.
如文档所述,Comparator
参数用于比较 Function
提取的值。这意味着 that Comparator
必须能够处理 null
。这与您似乎想要做的相反。
无需手动编写代码即可实现 null
安全 Comparator
的一种方法是使用 Comparator#nullsFirst(Comparator)
or Comparator#nullsLast(Comparator)
。这两个方法都包装了另一个 Comparator
,如果要比较的两个元素都不是 null
,将被调用。基本上,nullsFirst
或 nullsLast
返回的 Comparator
是一个拦截器。
现在只需要 Comparator
将被 null
-safe Comparator
包装。由于Integer
和BigDecimal
都是Comparable
,我们可以使用Comparator#naturalOrder()
.
假设 id
和 cost
都可以是 null
,并且您希望 null
值在每种情况下都排在第一位,那么以下内容应该适合您:
comparing(p -> p.id, nullsFirst(naturalOrder()))
.thenComparing(p -> p.cost, nullsFirst(naturalOrder()));
或者,如果您假设 id
永远不会是 null
,您可以像这样修改上面的内容:
comparing(p -> p.id)
.thenComparing(p -> p.cost, nullsFirst(naturalOrder()));