更改包含特殊字符(例如“_”)的字符串包含项的排序顺序

4
一个PHP脚本输出了一个按降序排列的电子邮件地址列表,如下所示:
_abc_@testmail.com
_abc45_@testmail.com
_abc2_@testmail.com
ypaux2aux@yahoo.com
yaremchuk56@testmail.com
vasillevn@hotmail.com
ugur@hotmail.com
twes@gmail.com
tukaux@yahoo.com
ttsetaux1@yahoo.com
tra@testmail.com

在Java中,我正在从这些电子邮件创建一个ArrayList,然后按降序排序。结果是不同的:
ypaux2aux@yahoo.com
yaremchuk56@testmail.com
vasillevn@hotmail.com
ugur@hotmail.com
twes@gmail.com
tukaux@yahoo.com
ttsetaux1@yahoo.com
tra@testmail.com
_abc45_@testmail.com
_abc2_@testmail.com
_abc_@testmail.com

由于下划线“_”的存在,导致排序方式不同。我想实现与PHP脚本相同的排序方式。但是我无法访问PHP代码。
我使用的Java测试代码如下:
import java.util.ArrayList;
import java.util.Collections;

public class sorty {

    public static void main(String[] args) {
        ArrayList<String> listStrings = new ArrayList<>();

        listStrings.add("_abc_@testmail.com");
        listStrings.add("_abc45_@testmail.com");
        listStrings.add("_abc2_@testmail.com");
        listStrings.add("ypaux2aux@yahoo.com");
        listStrings.add("yaremchuk56@testmail.com");
        listStrings.add("vasillevn@hotmail.com");
        listStrings.add("ugur@hotmail.com");
        listStrings.add("twes@gmail.com");
        listStrings.add("tukaux@yahoo.com");
        listStrings.add("ttsetaux1@yahoo.com");
        listStrings.add("tra@testmail.com");

        for (int i = 0; i < listStrings.size(); i++) {

            System.out.println(listStrings.get(i));

        }

        Collections.sort(listStrings);
        Collections.reverse(listStrings);

        for (int i = 0; i < listStrings.size(); i++) {

            System.out.println(listStrings.get(i));

        }
        ;

    }

}
2个回答

7
我会使用适当的Collator。自己实现比较器并不是最简单的事情。最好的办法是选择其中一个默认值,例如:
Collections.sort(listStrings, Collator.getInstance(Locale.US));

或者类似的。

如果现有的没有适合您的,那么使用基于规则的排序器会使您的意图更加清晰,而不是实现一个比较器:

String rules = "< a < b < c < '_'" //etc
Collections.sort(listStrings, new RuleBasedCollator(rules));

1
Locale.* 不适合我的例子。正如你所说,我可能需要使用 RuleBasedCollator。谢谢 @monocell - Turcia

3
使用一个理解下划线是特殊字符的自定义比较器进行排序:
Collections.sort(listStrings, new Comparator<String>() {

    @Override
    public int compare(String o1, String o2) {
        if (o1.startsWith("_") && o2.startsWith("_")) {
            return compare(o1.substring(1), o2.substring(1));
        }

        if (o1.startsWith("_")) {
            return 1;
        }
        if (o2.startsWith("_")) {
            return -1;
        }

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

这也可以处理存在多个下划线的情况。例如__foo将在_foo之后考虑。


为了应对任意数量的特殊字符,请将它们定义在数组中(按您喜欢的顺序),并使用更高级的比较器:

Collections.sort(listStrings, new Comparator<String>() {

    // declare in order of desired sort
    private final String[] specialChars = { "_", ">" };

    @Override
    public int compare(String o1, String o2) {
        /*
         * CASES
         * 
         * 1. Both start with same special char
         * 
         * 2. Both start with a special char
         * 
         * 3. One starts with a special char
         * 
         * 4. None starts with a special char
         */

        int o1SpecialIndex = -1;
        int o2SpecialIndex = -1;

        for (int i = 0; i < specialChars.length; i++) {
            if (o1.startsWith(specialChars[i])) {
                o1SpecialIndex = i;
            }
            if (o2.startsWith(specialChars[i])) {
                o2SpecialIndex = i;
            }
        }

        // case 1:
        if (o1SpecialIndex != -1 && o1SpecialIndex == o2SpecialIndex) {
            return compare(o1.substring(1), o2.substring(1));
        }

        // case 2:
        if (o1SpecialIndex != -1 && o2SpecialIndex != -1) {
            return o2SpecialIndex - o1SpecialIndex;
        }

        // case 3:
        if (o1SpecialIndex != -1) {
            return 1;
        }
        if (o2SpecialIndex != -1) {
            return -1;
        }

        // case 4:
        return o1.compareTo(o2);
    }
});

谢谢@Duncan;是否有一种方法可以将它用于所有特殊字符,例如“-”、“<”?有时下划线位于字符串的中间。 - Turcia
谢谢@Duncan。如果字符串以特殊字符开头,这个方法是可行的。但对于其他情况则不适用。 - Turcia
1
@Turcia 哦,我明白了。如果特殊字符出现在字符串内部,您需要保留相同的顺序? - Duncan Jones
是的,没错。我忘记在问题中添加一些示例电子邮件,例如 some_thing@mail.com - Turcia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接