Сортировка символов в строке 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 и выше.
Дополнительные источники: