Сортировка символов в строке UTF-16 в Java

TL;DR

Java использует два символа для представления UTF-16. Использование Arrays.sort (нестабильная сортировка) портит последовательность символов. Должен ли я конвертировать char [] в int [] или есть лучший способ?

подробности

Java представляет символ как UTF-16. Но сам класс Character упаковывает char (16 бит). Для UTF-16 это будет массив из двух char (32 бит).

Сортировка строки символов UTF-16 с использованием встроенной сортировки портит данные. (Arrays.sort использует быструю сортировку с двумя поворотами, а Collections.sort использует Arrays.sort для выполнения тяжелой работы.)

Если быть точным, вы конвертируете char [] в int [] или есть лучший способ сортировки?

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] utfCodes = {128513, 128531, 128557};
        String emojis = new String(utfCodes, 0, 3);
        System.out.println("Initial String: " + emojis);

        char[] chars = emojis.toCharArray();
        Arrays.sort(chars);
        System.out.println("Sorted String: " + new String(chars));
    }
}

Выход:

Initial String: 😁😓😭
Sorted String: ??😁??

Ответ 1

Я немного осмотрелся и не смог найти никаких простых способов сортировки массива по группам из двух элементов без использования библиотеки.

К счастью, codePoints String - это то, что вы использовали для создания самой String в этом примере, так что вы можете просто отсортировать их и создать новую String с результатом.

public static void main(String[] args) {
    int[] utfCodes = {128531, 128557, 128513};
    String emojis = new String(utfCodes, 0, 3);
    System.out.println("Initial String: " + emojis);

    int[] codePoints = emojis.codePoints().sorted().toArray();
    System.out.println("Sorted String: " + new String(codePoints, 0, 3));
}

Начальная строка: 😓😭😁

Сортированная строка: 😁😓😭

Я изменил порядок символов в вашем примере, потому что они уже отсортированы.

Ответ 2

Если вы используете Java 8 или более позднюю версию, то это простой способ сортировки символов в строке с соблюдением (не ломая) кодов с несколькими символами:

int[] codepoints = someString.codePoints().sort().toArray();
String sorted = new String(codepoints, 0, codepoints.length);

До Java 8 я думаю, что вам нужно либо использовать цикл для итерации кодовых точек в исходной строке, либо использовать сторонний библиотечный метод.


К счастью, сортировка кодовых точек в строке является достаточно редким явлением, так что неуклюжесть и относительная неэффективность приведенных выше решений редко вызывают озабоченность.

(Когда вы в последний раз проверяли анаграммы смайликов?)

Ответ 3

Мы не можем использовать char для Unicode, потому что обработка символов Java Unicode нарушена.

В первые дни Java кодовые точки Unicode всегда были 16-битными (фиксированный размер ровно на одном символе). Однако спецификация Unicode была изменена, чтобы разрешить использование дополнительных символов. Это означало, что символы Юникода теперь имеют переменную ширину и могут быть длиннее одного символа. К сожалению, было слишком поздно менять реализацию Java-символов, не нарушая тонны производственного кода.

Таким образом, лучший способ манипулировать символами Unicode - это непосредственно использовать кодовые точки, например, используя String.codePointAt(index) или String.codePoints() в JDK 1.8 и выше.

Дополнительные источники: