Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/373.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何简化空安全compareTo()实现?_Java_Refactoring_Comparison_Null_Compareto - Fatal编程技术网

Java 如何简化空安全compareTo()实现?

Java 如何简化空安全compareTo()实现?,java,refactoring,comparison,null,compareto,Java,Refactoring,Comparison,Null,Compareto,我正在为这样一个简单的类实现compareTo()方法(以便能够使用Collections.sort()和Java平台提供的其他功能): 这就可以了,但我对这段代码不是很满意。诚然,它并不十分复杂,但相当冗长乏味 问题是,在保留功能的同时,您如何使其不那么冗长?如果Java标准库或Apache Commons有帮助,请随时参考。让这个(稍微)简单一点的唯一选择是实现我自己的“NullSafeStringComparator”,并将其应用于比较两个字段吗 编辑1-3:埃迪的右图;修复了上面的“两个

我正在为这样一个简单的类实现
compareTo()
方法(以便能够使用
Collections.sort()
和Java平台提供的其他功能):

这就可以了,但我对这段代码不是很满意。诚然,它并不十分复杂,但相当冗长乏味

问题是,在保留功能的同时,您如何使其不那么冗长?如果Java标准库或Apache Commons有帮助,请随时参考。让这个(稍微)简单一点的唯一选择是实现我自己的“NullSafeStringComparator”,并将其应用于比较两个字段吗

编辑1-3:埃迪的右图;修复了上面的“两个名称都为空”的情况

关于公认的答案 我在2009年问过这个问题,当然是在Java1.6上,当时是我首选的公认答案。直到现在(2017年),我才抽出时间来改变这一点

还有——2009年的Apache Commons Collections one和2013年的Guava one,都是我在某个时候发布的,我确实更喜欢


现在,我把公认的答案澄清了。如果在Java 8上,这肯定是首选,现在Java 8应该可以用于几乎所有的项目。

您可以将类设计为不可变的(有效的Java第二版。关于这一点有一个很好的章节,第15项:最小化可变性),并确保在构建时不可能出现空值(如果需要,使用空值)。然后您可以跳过所有这些检查,并安全地假设值不为空。

我将实现一个空安全比较器。可能有一个实现,但这是如此简单的实现,我一直在推出自己的

注意:上面的比较器,如果两个名称都为空,甚至不会比较值字段。我认为这不是你想要的

我将通过以下方式实现这一点:

// 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);
}
编辑:修复了代码示例中的打字错误。这就是我没有先测试的原因


编辑:将nullSafeStringComparator升级为静态。

您可以提取方法:

public int cmp(String txt, String otherTxt)
{
    if ( txt == null )
        return otjerTxt == 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); 

}

我一直建议使用Apache commons,因为它很可能比您自己编写的更好。此外,你还可以做“真正的”工作,而不是重新发明

你感兴趣的课程是。它允许您将空值设为高或低。当两个值不为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);
}
类元数据实现了可比较的{
私有字符串名称;
私有字符串值;
静态NullComparator nullAndCaseInsensitveComparator=新NullComparator(
新比较器(){
@凌驾
公共整数比较(字符串o1、字符串o2){
//输入不能为空
返回o1.比较信号(o2);
}
});
@凌驾
公共整数比较(元数据其他){
如果(其他==null){
返回1;
}
int res=null和caseinsensitvecomparator.compare(name,other.name);
如果(res!=0)
返回res;
返回null和caseinsensitvecomparator.compare(value,other.value);
}
}


即使您决定使用自己的类,也要记住这个类,因为它在排序包含空元素的列表时非常有用。

有关使用番石榴的更新(2013)解决方案,请参见此答案的底部


这就是我最终选择的。事实证明,我们已经有了一个用于空安全字符串比较的实用方法,所以最简单的解决方案就是利用它。(这是一个很大的代码库;很容易错过这种东西:)

这就是辅助对象的定义方式(它是重载的,因此如果需要,您还可以定义null是第一个还是最后一个):

因此,这本质上与(尽管我不会将静态助手方法称为比较器)相同

总之,总的来说,我会非常赞成,因为我认为尽可能使用现有的库是一种好的做法。(正如Josh Bloch所说,了解并使用这些库。)但在这种情况下,这不会产生最干净、最简单的代码

编辑(2009):Apache Commons集合版本 实际上,这里有一种方法可以简化基于ApacheCommons的解决方案。将其与
字符串
类中提供的组合:

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);
}
然后,在
中,公共类元数据实现了可比较的

当然,这与Apache Commons版本几乎相同(两者都使用 使用
nullsLast()
是番石榴特有的东西。这个版本更可取,只是因为番石榴作为一种依赖物,比Commons系列更可取。(如图所示)

如果您想知道,请注意它实现了
Comparator
。它非常方便,特别是对于更复杂的排序需求,例如允许您使用
component()
链接多个订单。阅读更多

您可以简单地使用:


我知道这可能不是对您问题的直接回答,因为您说过必须支持空值

但我只想指出,在compareTo中支持null并不符合官方文件中描述的compareTo合同:

注意null不是任何类的实例,e.compareTo(null) 即使e.equals(null)返回,也应引发NullPointerException 错


因此,我要么显式抛出NullPointerException,要么在取消引用null参数时第一次抛出它。

使用Java8

专用静态比较器nullSafeStringComparator=比较器
.nullsFirst(字符串::compareToIgnoreCase);
专用静态比较器metadataComparator=比较器
.comparing(元数据::getName,nullSa)
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);
}
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) { ... }
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);
}
public static final Ordering<String> CASE_INSENSITIVE_NULL_SAFE_ORDER =
    Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast(); // or nullsFirst()
@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);
}    
result = ObjectUtils.compare(firstComparable, secondComparable)
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);
    }
}
@Override
public int compare(Object o1, Object o2) {
    String s1 = ObjectUtils.toString(o1);
    String s2 = ObjectUtils.toString(o2);
    return s1.toLowerCase().compareTo(s2.toLowerCase());
}
@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;
}
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
        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);
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]
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;
    }
arrlist.sort((o1, o2) -> {
    if (o1.getName() == null) o1.setName("");
    if (o2.getName() == null) o2.setName("");

    return o1.getName().compareTo(o2.getName());
})
return o2.getName().compareTo(o1.getName());