Можно ли изменить приоритет (вес) определенных символов при сортировке? - PullRequest
0 голосов
/ 05 ноября 2018

Я хочу создать компаратор для строковых значений, но согласно https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html символ подчеркивания имеет большее значение, чем любая цифра. Можно ли это как-то изменить?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Краткий ответ

Да, это так. Вам нужен собственный компаратор строк.

Решение

Допустим, вам нужно отсортировать список строк:

[a_123, ab123, a123, 123, _123]

Если вы сортируете его, используя Collections.sort, тогда он будет отсортирован в следующем порядке:

[123, _123, a123, a_123, ab123]

Но вы хотите переопределить «вес» _. Для этого вам нужен собственный компаратор строк. Давайте скопируем и изменим немного java.lang.String#compareTo:

private int customStringComparator(String s1, String s2) {
    int len1 = s1.length();
    int len2 = s2.length();
    int lim = Math.min(len1, len2);
    char v1[] = s1.toCharArray();
    char v2[] = s2.toCharArray();

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        // You can add your custom comparison here:
        if ('_' == c1 && Character.isDigit(c2)) {
            // We intentionally return inverted result
            return c2  - c1;
        }else if(c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    return len1 - len2;
}

Теперь мы можем передать customStringComparator на Collections.sort:

Collections.sort(list, this::customStringComparator);

Список будет отсортирован в следующем порядке:

[_123, 123, a_123, a123, ab123]

Как видите, теперь _ предшествует цифрам.

0 голосов
/ 05 ноября 2018

Это может сработать:

private int compareStrings(String o1, String o2) {
    if(o1.matches("\\d+") && o2.equals("_")) {
        return 1;
    }
    if(o1.equals("_") && o2.matches("\\d+")) {
        return -1;
    }
    return o1.compareTo(o2);
}

А затем определите свой компаратор следующим образом:

Comparator<String> stringComparator2 = this::compareStrings;

EDIT:

В соответствии с тем, что не работает для строки с _ в середине, как насчет простой замены _ символом, который находится перед таблицей ASCII, просто для сравнения (как, например, " "):

public static int compareStrings(String o1, String o2) {
    o1 = o1.replaceAll("_", " ");
    o2 = o2.replaceAll("_", " ");
    return o1.compareTo(o2);
}
...