null-safe compareTo() 구현을 단순화하는 방법
하고 있어요.compareTo()
할 수 경우)Collections.sort()
자바:
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
이러한 오브젝트의 자연스러운 순서를 1) 이름별로 정렬하고 2) 이름이 같을 경우 값별로 정렬합니다.대소문자를 구분하지 않습니다.두 필드 모두 null 값은 완벽하게 허용됩니다.compareTo
이 경우 파손되지 않아야 합니다.
갑자기 떠오르는 솔루션은 다음과 같습니다(여기에서는 "guard 절"을 사용하지만 다른 사람들은 단일 반환점을 선호할 수 있지만 요점을 벗어납니다).
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
이거면 되는데 이 코드가 마음에 안 들어.물론 그것은 매우 복잡하지는 않지만 꽤 장황하고 지루하다.
문제는 (기능을 유지한 채) 어떻게 하면 이 문제를 덜 상세하게 만들 수 있을까 하는 것입니다.Java 표준 라이브러리 또는 Apache Commons가 도움이 될 경우 언제든지 참고하십시오.이것을 좀 더 간단하게 하기 위한 유일한 옵션은 나만의 "Null Safe String Comparator"를 구현하여 두 필드를 비교하는 것입니까?
편집 1-3: Eddie 오른쪽. 위의 "둘 다 null" 대소문자를 수정했습니다.
승인된 답변에 대하여
2009년에 Java 1.6에서 이 질문을 한 적이 있습니다.당시 Eddie의 순수 JDK 솔루션이 제가 선호하는 답변이었습니다.저는 지금까지(2017년)까지 그것을 바꿀 여유가 없었습니다.
서드파티 라이브러리 솔루션(2009년 Apache Commons Collections 1개 및 2013년 Guava 1개)도 있습니다.둘 다 제가 올린 것입니다.
저는 이제 Lukasz Wiktor의 깨끗한 Java 8 솔루션을 인정받는 답변으로 만들었습니다.Java 8을 사용하는 경우, Java 8을 사용하는 것이 바람직하며, 최근에는 거의 모든 프로젝트에서 Java 8을 사용할 수 있게 되었습니다.
Apache Commons Lang을 사용하면 됩니다.
result = ObjectUtils.compare(firstComparable, secondComparable)
Java 8 사용:
private static Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareToIgnoreCase);
private static Comparator<Metadata> metadataComparator = Comparator
.comparing(Metadata::getName, nullSafeStringComparator)
.thenComparing(Metadata::getValue, nullSafeStringComparator);
public int compareTo(Metadata that) {
return metadataComparator.compare(this, that);
}
나는 null safe comparator를 구현할 것이다.구현이 있을 수 있지만, 구현이 매우 간단하기 때문에 저는 항상 자신의 것을 롤업해 왔습니다.
주의: 위의 비교기에서는 두 이름이 모두 null일 경우 값 필드는 비교조차 하지 않습니다.이게 네가 원하는 게 아닌 것 같아.
다음과 같은 방법으로 구현합니다.
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(final Metadata other) {
if (other == null) {
throw new NullPointerException();
}
int result = nullSafeStringComparator(this.name, other.name);
if (result != 0) {
return result;
}
return nullSafeStringComparator(this.value, other.value);
}
public static int nullSafeStringComparator(final String one, final String two) {
if (one == null ^ two == null) {
return (one == null) ? -1 : 1;
}
if (one == null && two == null) {
return 0;
}
return one.compareToIgnoreCase(two);
}
편집: 코드 샘플의 오타가 수정되었습니다.내가 먼저 시험해 보지 않은 게 그거야!
EDIT: null Safe String Comparator가 static으로 승격되었습니다.
Guava를 사용한 업데이트된(2013) 솔루션에 대해서는 이 답변의 하단을 참조하십시오.
이것이 내가 결국 선택한 것이다.알고 보니 null-safe String 비교에 대한 유틸리티 방법이 이미 있었기 때문에 가장 간단한 해결책은 그것을 이용하는 것이었습니다.(이것은 큰 코드 베이스입니다.이러한 것을 놓치기 쉽습니다).
public int compareTo(Metadata other) {
int result = StringUtils.compare(this.getName(), other.getName(), true);
if (result != 0) {
return result;
}
return StringUtils.compare(this.getValue(), other.getValue(), true);
}
도우미는 다음과 같이 정의됩니다(필요에 따라 늘이 처음인지 마지막인지 정의할 수 있도록 오버로드됩니다).
public static int compare(String s1, String s2, boolean ignoreCase) { ... }
이것은 Eddie의 답변(스태틱 헬퍼 방식은 비교기라고는 할 수 없지만)이나 uzhin의 답변과 기본적으로 동일합니다.
어쨌든, 가능한 한 기존의 라이브러리를 이용하는 것이 좋은 방법이라고 생각하기 때문에, 일반적으로 패트릭의 솔루션을 강하게 지지하고 싶다(Josh Bloch의 말대로 라이브러리를 알고 사용한다).하지만 이 경우 가장 깨끗하고 간단한 코드를 얻을 수 없었을 것입니다.
편집 (2009) :Apache Commons Collections 버전
실제로 Apache Commons에 기반한 솔루션을 보다 단순하게 만드는 방법이 있습니다.에서 제공하는 대소문자를 구분하지 않는 것과 조합합니다.String
링크:
public static final Comparator<String> NULL_SAFE_COMPARATOR
= new NullComparator(String.CASE_INSENSITIVE_ORDER);
@Override
public int compareTo(Metadata other) {
int result = NULL_SAFE_COMPARATOR.compare(this.name, other.name);
if (result != 0) {
return result;
}
return NULL_SAFE_COMPARATOR.compare(this.value, other.value);
}
이제 하원이라는 작은 문제입니다.★★★★★★ 。NullComparator
제네릭스를 지원하지 않기 때문에 선택되지 않은 할당이 있습니다.)
갱신(2013):구아바 버전
거의 5년이 지난 지금, 저의 원래 질문에 대한 답변은 이렇습니다.자바 코딩이라면 (물론) Guava를 사용하고 있을 것입니다.(그리고 Apache Commons는 확실히 아닙니다.)
이 상수는 StringUtils 클래스 등 어딘가에 둡니다.
public static final Ordering<String> CASE_INSENSITIVE_NULL_SAFE_ORDER =
Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast(); // or nullsFirst()
ㅇㅇㅇㅇ에서는요.public class Metadata implements Comparable<Metadata>
:
@Override
public int compareTo(Metadata other) {
int result = CASE_INSENSITIVE_NULL_SAFE_ORDER.compare(this.name, other.name);
if (result != 0) {
return result;
}
return CASE_INSENSITIVE_NULL_SAFE_ORDER.compare(this.value, other.value);
}
물론 Apache Commons 버전(둘 다 JDK의 CASE_INSECTIVE_ORDER 사용)과 거의 동일합니다.nullsLast()
구아바에 특화된 유일한 것입니다.이 버전은 단순히 Commons Collections보다 Guava가 종속성이 더 좋기 때문에 선호됩니다.(모두의 동의대로)
에 대해 궁금하신 경우 이 기능이 구현되어 있는 것에 주의해 주세요.Comparator
요구에 매우 , several several several several several several several several several several several 를 사용하여 여러 주문을 연결할 수 예를 들어 여러 주문을 체인으로 연결할 수 있습니다.compound()
자세한 내용은 주문 설명을 참조하십시오.
Apache Commons를 사용하는 것이 스스로 쓸 수 있는 것보다 더 나을 것 같기 때문에 항상 추천합니다.게다가 재창조하는 것이 아니라, 「실제」의 작업을 실시할 수 있습니다.
관심 있는 클래스는 Null Comparator입니다.null을 높거나 낮게 만들 수 있습니다.또한 두 값이 null이 아닐 때 사용할 자체 비교기를 지정할 수도 있습니다.
수 , 그 에는 비교를 수행할 수 있습니다.compareTo
는 그것을합니다.
뭐랄까
class Metadata implements Comparable<Metadata> {
private String name;
private String value;
static NullComparator nullAndCaseInsensitveComparator = new NullComparator(
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// inputs can't be null
return o1.compareToIgnoreCase(o2);
}
});
@Override
public int compareTo(Metadata other) {
if (other == null) {
return 1;
}
int res = nullAndCaseInsensitveComparator.compare(name, other.name);
if (res != 0)
return res;
return nullAndCaseInsensitveComparator.compare(value, other.value);
}
}
자체 롤링을 결정한 경우에도 이 클래스는 null 요소가 포함된 목록을 주문할 때 매우 유용하므로 유의하십시오.
null 값은 지원되어야 한다고 하셨기 때문에 질문에 대한 직접적인 답변이 아닐 수 있습니다.
단, compareTo에서 null을 지원하는 것은 Comparatible 공식 javadocs에서 설명된 comparTo 계약과 일치하지 않습니다.
null은 어떤 클래스의 인스턴스가 아니며 e.compareTo(null)는 NullPointer를 슬로우해야 합니다.e.equals(null)가 false를 반환해도 예외입니다.
그래서 Null Pointer를 던지거나null 인수가 참조되지 않을 경우 명시적으로 또는 처음 던지는 것으로 합니다.
메서드를 추출할 수 있습니다.
public int cmp(String txt, String otherTxt)
{
if ( txt == null )
return otherTxt == null ? 0 : 1;
if ( otherTxt == null )
return 1;
return txt.compareToIgnoreCase(otherTxt);
}
public int compareTo(Metadata other) {
int result = cmp( name, other.name);
if ( result != 0 ) return result;
return cmp( value, other.value);
}
클래스를 불변하게 설계할 수 있습니다(유효한 Java 2nd Ed).에는, 이것에 관한 「항목 15:변동성의 최소화」의 항이 있어, 구축시에 늘이 불가능한 것을 확인해 주세요(필요한 경우는 늘 오브젝트 패턴을 사용합니다).그런 다음 모든 검사를 건너뛰고 값이 null이 아니라고 가정할 수 있습니다.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Comparator;
public class TestClass {
public static void main(String[] args) {
Student s1 = new Student("1","Nikhil");
Student s2 = new Student("1","*");
Student s3 = new Student("1",null);
Student s11 = new Student("2","Nikhil");
Student s12 = new Student("2","*");
Student s13 = new Student("2",null);
List<Student> list = new ArrayList<Student>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s11);
list.add(s12);
list.add(s13);
list.sort(Comparator.comparing(Student::getName,Comparator.nullsLast(Comparator.naturalOrder())));
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Student student = (Student) iterator.next();
System.out.println(student);
}
}
}
출력은
Student [name=*, id=1]
Student [name=*, id=2]
Student [name=Nikhil, id=1]
Student [name=Nikhil, id=2]
Student [name=null, id=1]
Student [name=null, id=2]
비슷한 걸 찾다가 이게 좀 복잡해 보여서 이렇게 했어요.그게 좀 더 이해하기 쉬울 것 같아요.비교기 또는 단일 라이너로 사용할 수 있습니다.이 질문에서는 compareToIgnoreCase()로 변경합니다.늘은 그대로 떠다닌다.1, -1을 가라앉히려면 뒤집을 수 있습니다.
StringUtil.NULL_SAFE_COMPARATOR.compare(getName(), o.getName());
.
public class StringUtil {
public static final Comparator<String> NULL_SAFE_COMPARATOR = new Comparator<String>() {
@Override
public int compare(final String s1, final String s2) {
if (s1 == s2) {
//Nulls or exact equality
return 0;
} else if (s1 == null) {
//s1 null and s2 not null, so s1 less
return -1;
} else if (s2 == null) {
//s2 null and s1 not null, so s1 greater
return 1;
} else {
return s1.compareTo(s2);
}
}
};
public static void main(String args[]) {
final ArrayList<String> list = new ArrayList<String>(Arrays.asList(new String[]{"qad", "bad", "sad", null, "had"}));
Collections.sort(list, NULL_SAFE_COMPARATOR);
System.out.println(list);
}
}
Spring을 사용하는 사람이 있다면 org.springframework.util.comparator라는 클래스가 있습니다.NullSafeComparator는 이 기능을 제공합니다.이것과 비교할 수 있는 나만의 것을 이렇게 꾸밀 수 있다.
new NullSafeComparator<YourObject>(new YourComparable(), true)
Java 8을 사용하여 오브젝트 간의 null-friendly 비교를 수행할 수 있습니다.나는 두 개의 필드가 있는 남자 클래스가 있다고 가정합니다.String name과 Integer age를 먼저 비교한 후 둘 다 동일한 경우 age를 비교합니다.
static void test2() {
List<Boy> list = new ArrayList<>();
list.add(new Boy("Peter", null));
list.add(new Boy("Tom", 24));
list.add(new Boy("Peter", 20));
list.add(new Boy("Peter", 23));
list.add(new Boy("Peter", 18));
list.add(new Boy(null, 19));
list.add(new Boy(null, 12));
list.add(new Boy(null, 24));
list.add(new Boy("Peter", null));
list.add(new Boy(null, 21));
list.add(new Boy("John", 30));
List<Boy> list2 = list.stream()
.sorted(comparing(Boy::getName,
nullsLast(naturalOrder()))
.thenComparing(Boy::getAge,
nullsLast(naturalOrder())))
.collect(toList());
list2.stream().forEach(System.out::println);
}
private static class Boy {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boy(String name, Integer age) {
this.name = name;
this.age = age;
}
public String toString() {
return "name: " + name + " age: " + age;
}
}
그 결과:
name: John age: 30
name: Peter age: 18
name: Peter age: 20
name: Peter age: 23
name: Peter age: null
name: Peter age: null
name: Tom age: 24
name: null age: 12
name: null age: 19
name: null age: 21
name: null age: 24
데이터가 늘이 아니고(문자열에 대해서는 항상 좋은 아이디어) 데이터가 매우 큰 경우, 실제 값을 비교하기 전에 세 가지 비교를 계속하고 있습니다.만약 이것이 확실히 고객의 경우라면 조금 최적화할 수 있습니다.판독 가능한 코드가 마이너 최적화를 능가하는 YMMV:
if(o1.name != null && o2.name != null){
return o1.name.compareToIgnoreCase(o2.name);
}
// at least one is null
return (o1.name == o2.name) ? 0 : (o1.name != null ? 1 : -1);
Null Safe Comparator를 사용하는 간단한 방법 중 하나는 스프링 구현을 사용하는 것입니다.다음은 참조하는 간단한 예 중 하나입니다.
public int compare(Object o1, Object o2) {
ValidationMessage m1 = (ValidationMessage) o1;
ValidationMessage m2 = (ValidationMessage) o2;
int c;
if (m1.getTimestamp() == m2.getTimestamp()) {
c = NullSafeComparator.NULLS_HIGH.compare(m1.getProperty(), m2.getProperty());
if (c == 0) {
c = m1.getSeverity().compareTo(m2.getSeverity());
if (c == 0) {
c = m1.getMessage().compareTo(m2.getMessage());
}
}
}
else {
c = (m1.getTimestamp() > m2.getTimestamp()) ? -1 : 1;
}
return c;
}
다른 Apache ObjectUtils의 예.다른 유형의 개체를 정렬할 수 있습니다.
@Override
public int compare(Object o1, Object o2) {
String s1 = ObjectUtils.toString(o1);
String s2 = ObjectUtils.toString(o2);
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
이것은 Array List를 정렬하기 위해 사용하는 구현입니다.null 클래스는 마지막까지 정렬됩니다.
내 경우 엔티티전화기는 EntityAbstract를 확장하며 컨테이너는 List <EntityAbstract>입니다.
compareIfNull() 메서드는 늘 세이프 정렬에 사용됩니다.다른 방법은 compareIfNull을 사용하는 방법을 보여 주는 완전성을 위한 방법입니다.
@Nullable
private static Integer compareIfNull(EntityPhone ep1, EntityPhone ep2) {
if (ep1 == null || ep2 == null) {
if (ep1 == ep2) {
return 0;
}
return ep1 == null ? -1 : 1;
}
return null;
}
private static final Comparator<EntityAbstract> AbsComparatorByName = = new Comparator<EntityAbstract>() {
@Override
public int compare(EntityAbstract ea1, EntityAbstract ea2) {
//sort type Phone first.
EntityPhone ep1 = getEntityPhone(ea1);
EntityPhone ep2 = getEntityPhone(ea2);
//null compare
Integer x = compareIfNull(ep1, ep2);
if (x != null) return x;
String name1 = ep1.getName().toUpperCase();
String name2 = ep2.getName().toUpperCase();
return name1.compareTo(name2);
}
}
private static EntityPhone getEntityPhone(EntityAbstract ea) {
return (ea != null && ea.getClass() == EntityPhone.class) ?
(EntityPhone) ea : null;
}
간단한 해킹을 원하는 경우:
arrlist.sort((o1, o2) -> {
if (o1.getName() == null) o1.setName("");
if (o2.getName() == null) o2.setName("");
return o1.getName().compareTo(o2.getName());
})
목록의 끝에 null을 붙이려면 위의 metod에서 이것을 변경하세요.
return o2.getName().compareTo(o1.getName());
언급URL : https://stackoverflow.com/questions/481813/how-to-simplify-a-null-safe-compareto-implementation
'programing' 카테고리의 다른 글
데이터 표시 가능한 여러 행 선택(Shift+Click) (0) | 2022.07.21 |
---|---|
JAVA_를 설정하는 방법MacOS X 10.6의 홈 환경 변수 (0) | 2022.07.21 |
어떤 자바 섬에 있는 C#'var의 키워드의 가치야? (0) | 2022.07.21 |
Javascript에서 숫자의 절대 값 가져 오기 (0) | 2021.01.17 |
다른 컴퓨터에서 ipython에 대한 연결을 수락하는 방법은 무엇입니까? (0) | 2021.01.17 |