如何通过多个字段改进对象的比较器

时间:2019-05-31 16:37:03

标签: java sorting comparator

如何通过几个字段改进对象的比较器?如果姓氏不存在,比较器必须按姓氏或名字对用户进行排序。如果没有姓和名,则按用户名排序。但是这些用户必须位于列表的末尾。

public static Comparator<UserConfigurationDto> BY_LASTNAME =  (u1, u2) -> {
        // if users have only username compare them
        if((u1.getLastName().isEmpty() && u1.getFirstName().isEmpty())
                && (u2.getLastName().isEmpty() && u2.getFirstName().isEmpty())){
            return u1.getUsername().compareToIgnoreCase(u2.getUsername());
        }
        //if user doesnt have firstName and LastName drop them at the end
        if(u1.getLastName().isEmpty() && u1.getFirstName().isEmpty()){
            return 1000000 + getWeight(u1.getUsername());
        }
        if(u2.getLastName().isEmpty() && u2.getFirstName().isEmpty()){
            return -1000000 + getWeight(u2.getUsername());
        }
      String s1 = u1.getLastName().isEmpty() ? u1.getFirstName() : u1.getLastName();
      String s2 = u2.getLastName().isEmpty() ? u2.getFirstName() : u2.getLastName();
      return s1.compareToIgnoreCase(s2);
    };
}
private static int getWeight(String s){
    return s.codePoints().sum();
}

有人知道如何改善这一点吗?我尝试使用Comparator.comparing和Comparator.thenComparing,但它们会产生错误的结果

4 个答案:

答案 0 :(得分:1)

1)不需要return 1000000 + getWeight(u1.getUsername());return -1000000 + getWeight(u2.getUsername());。如果您参考return 1 javadoc,则return -1CompareTo()会更清晰,并产生相同的结果:

  

将此对象与指定对象进行比较。返回一个   整数,整数,因为此对象较少   大于,等于或大于指定的对象

2)您没有链接字段比较,但是根据比较对象的状态有3种排序方式。因此,定义每个案例的代码有点冗长这一事实最终是正常的。
复制很多user.getLastName().isEmpty()调用时,您都可以使用提取方法来减少它。

例如:

public static Comparator<UserConfigurationDto> BY_LASTNAME =  (u1, u2) -> {

        // first case
        if( u1.isLastAndFirstNameEmpty() && u2.isLastAndFirstNameEmpty()){
            return u1.getUsername().compareToIgnoreCase(u2.getUsername());
        }
        // second case
        if(u1.isLastAndFirstNameEmpty()){
            return 1;
        }
        else if(u2.isLastAndFirstNameEmpty()){
            return -1;
        }
        // third case
        String s1 = u1.getLastName().isEmpty() ? u1.getFirstName() : u1.getLastName();
        String s2 = u2.getLastName().isEmpty() ? u2.getFirstName() : u2.getLastName();
        return s1.compareToIgnoreCase(s2);
    };

答案 1 :(得分:1)

您要去哪里的地方是尝试应用“重量”的概念。用Java处理比较器时,从函数中出来的唯一重要的值是0,大于0和小于0。

类似的事情似乎起作用:

public static final Comparator<UserConfgurationDto> BY_LASTNAME = ( u1, u2 ) ->
{
    boolean u1hasName = !u1.getLastName().isEmpty() || !u1.getFirstName().isEmpty();
    boolean u2hasName = !u2.getLastName().isEmpty() || !u2.getFirstName().isEmpty();

    if ( u1hasName && !u2hasName )
    {
        // u1 < u2
        return -1;
    }
    else if ( !u1hasName && u2hasName )
    {
        // u2 < u1
        return 1;
    }
    else if ( u1hasName && u2hasName )
    {
        String s1 = u1.getLastName().isEmpty() ? u1.getFirstName() : u1.getLastName();
        String s2 = u2.getLastName().isEmpty() ? u2.getFirstName() : u2.getLastName();

        return s1.compareToIgnoreCase( s2 );
    }
    else
    {
        return u1.getUsername().compareToIgnoreCase( u2.getUsername() );
    }
};

答案 2 :(得分:1)

我认为您可以使用Java 8静态comparing function that includes the key comparator来实现此目的。 String有一个方便的case insensitive comparator,您也可以在几个地方使用它。

Comparator.comparing(
                (UserConfigurationDto u) -> u.getLastName().isEmpty()?u.getFirstName():u.getLastName(),
                ((s1, s2) -> {
                    if(s1.isEmpty()) {
                        return 1;
                    } else if(s2.isEmpty()) {
                        return -1;
                    }
                    else {
                        return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
                    }
                }))
                .thenComparing(UserConfigurationDto::getUsername, String.CASE_INSENSITIVE_ORDER);

答案 3 :(得分:0)

heisbrandon的答案为基础,但进一步考虑到两个空字符串相等(因此将触发对thenComparing的调用),您实际上可以简化代码。假设比较两个姓氏缺失的用户与比较两个姓氏相同的用户,等等。

public static Comparator<UserConfigurationDto> BY_LASTNAME = 
        Comparator.comparing(UserConfigurationDto::getLastName)
        .thenComparing(UserConfigurationDto::getFirstName)
        .thenComparing(UserConfigurationDto::getUsername);