web-dev-qa-db-fra.com

Tri des caractères dans une chaîne UTF-16 dans Java

TLDR

Java utilise deux caractères pour représenter UTF-16. L'utilisation de Arrays.sort (tri instable) perturbe le séquençage des caractères. Dois-je convertir char [] en int [] ou existe-t-il une meilleure façon?

Détails

Java représente un personnage comme UTF-16. Mais la classe Character elle-même enveloppe char (16 bits). Pour UTF-16, ce sera un tableau de deux chars (32 bits).

Le tri d'une chaîne de caractères UTF-16 à l'aide du tri intégré dérange les données. (Arrays.sort utilise le tri rapide à double pivot et Collections.sort utilise Arrays.sort pour faire le gros du travail.)

Pour être précis, convertissez-vous char [] en int [] ou existe-t-il une meilleure façon de trier?

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));
    }
}

Production:

Initial String: ????????????
Sorted String: ????????
16
dingy

J'ai regardé un peu et je n'ai pas trouvé de moyens propres de trier un tableau par regroupement de deux éléments sans utiliser de bibliothèque.

Heureusement, les codePoints des String sont ce que vous avez utilisé pour créer le String lui-même dans cet exemple, vous pouvez donc simplement les trier et créer un nouveau String Avec le résultat.

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));
}

Chaîne initiale: ????????????

Chaîne triée: ????????????

J'ai changé l'ordre des caractères dans votre exemple car ils étaient déjà triés.

12
Jacob G.

Si vous utilisez Java 8 ou version ultérieure, alors c'est un moyen simple de trier les caractères dans une chaîne tout en respectant (sans casser) les points de code multi-caractères:

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

Avant Java 8, je pense que vous devez soit utiliser une boucle pour itérer les points de code dans la chaîne d'origine, soit utiliser une méthode de bibliothèque tierce.


Heureusement, le tri des points de code dans une chaîne est assez rare pour que le clunkyness et l'inefficacité relative des solutions ci-dessus soient rarement un problème.

(À quand remonte la dernière fois que vous avez testé les anagrammes d'émojis?)

6
Stephen C

Nous ne pouvons pas utiliser char pour Unicode, car la gestion des chars Unicode de Java est cassée .

Dans les premiers jours de Java, les points de code Unicode étaient toujours 16 bits (taille fixe à exactement un caractère). Cependant, la spécification Unicode a changé pour autoriser les caractères supplémentaires. Cela signifie que les caractères Unicode ont désormais des largeurs variables et peuvent être plus longs qu'un caractère. Malheureusement, il était trop tard pour changer l'implémentation de Java sans casser une tonne de code de production.

Ainsi, la meilleure façon de manipuler les caractères Unicode est d'utiliser directement les points de code, par exemple, en utilisant String.codePointAt(index) ou le flux String.codePoints() sur JDK 1.8 et supérieur.

Sources supplémentaires:

4
peekay