Java 中是否有 Char 整理器?

Is there a Char collator in Java?

我正在开发一个小应用程序,它计算文本中字符的出现次数并打印一份简单的报告。它基于 TreeMap。它应该与任何 UTF-8(到目前为止)可编码语言一起使用。当我尝试通过调用 Collator.getInstance() 来使用标准整理器时,出现异常 java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.String.

有没有Char整理器?

static Map<Character, Integer> map = new TreeMap<>(); 

TreeMap 构造函数可以采用整理器,但不能用于字符。

public static void main(String[] args) {
    InputStream in = System.in;

    try {
        if (in.available() == 0) System.exit(0);
    } catch (IOException e) {
        e.printStackTrace();
    }

    count(in);  
    printMap();
} 


static void count(InputStream in) {
    new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))   
        .lines()
        .forEach(x -> tallyCharArray(x.toCharArray()));
}

static void tallyCharArray (char[] chars) {
    for (int i=0; i<chars.length; i++) {
        map.merge(chars[i], 1, Integer::sum);
    }
}

static void printMap() {
    map.entrySet().stream()
    .forEach(x -> System.out.println(x.getKey() + "\t" + x.getValue()));
}

比较有问题

static Map<Character, Integer> map = new TreeMap<>(
    Collator.getInstance().compare(String.valueOf(c1), String.valueOf(c2))
);

这很笨拙,还行不通。 c1c2如何绑定地图?

已更新

如果打印时只需要Collator对结果进行排序,只需在计数后排序即可。性能要好得多。进一步查看代码。

如果你想让TreeMap使用Collator,得到Collator,然后给TreeMap构造函数一个Comparator<Character>。由于您使用的是 Java 8 个流,因此您不妨使用 lambda 表达式来执行此操作:

Collator collator = Collator.getInstance(Locale.GERMAN);
collator.setStrength(Collator.PRIMARY);
Map<Character, int[]> countMap = new TreeMap<>(
        (c1, c2) -> collator.compare(c1.toString(), c2.toString())
);

使用 Collator,重音​​和 upper-/lower-case 字符全部合并。请参阅此答案末尾的示例输出。

计数后排序的完整代码

String input = "Das Polaritätsprofil für das Wort \"Hund\" als Testeinheit " +
               "könnte zeigen , dass verschiedene Personen unterschiedliche " +
               "Einstellungen zu diesen Tieren haben .";

Map<Character, int[]> countMap = new HashMap<>();
for (Character ch : input.toCharArray()) {
    int[] counter = countMap.get(ch);
    if (counter == null)
        countMap.put(ch, new int[] { 1 });
    else
        counter[0]++;
}
@SuppressWarnings("unchecked")
Entry<Character, int[]>[] counts = countMap.entrySet().toArray(new Map.Entry[countMap.size()]);
Collator collator = Collator.getInstance(Locale.GERMAN);
Arrays.sort(counts, (e1, e2) -> collator.compare(e1.getKey().toString(), e2.getKey().toString()));
for (Entry<Character, int[]> entry : counts)
    System.out.printf("%c - %d%n", entry.getKey(), entry.getValue()[0]);

计数后排序输出

, - 1
. - 1
" - 2
  - 20
a - 6
ä - 1
b - 1
c - 3
d - 6
D - 1
E - 1
e - 22
f - 2
g - 2
h - 5
H - 1
i - 11
k - 1
l - 6
n - 15
ö - 1
o - 4
P - 2
p - 1
r - 8
s - 12
t - 8
T - 2
u - 4
ü - 1
v - 1
W - 1
z - 2

可以看到,结果按照德文排序打印,ä介于ab之间。

如果你想统一大小写字符,你应该在结果中决定你想要哪个并转换成那个,否则它是任意的。

TreeMap

中使用PRIMARYCollator的输出
  - 20
, - 1
. - 1
" - 2
a - 7
b - 1
c - 3
D - 7
e - 23
f - 2
g - 2
H - 6
i - 11
k - 1
l - 6
n - 15
o - 5
P - 3
r - 8
s - 12
t - 10
ü - 5
v - 1
W - 1
z - 2

如您所见,有时您会得到一个小写字母(例如 a),有时您会得到一个大写字母(例如 D),有时您会得到一个重音字母(例如 [= =29=]). 这对我来说似乎是错误的。

A​​ char 是 UTF-16 格式的 2 字节值。 Unicode 符号,代码点,达到 3 字节范围,在 java 中表示为 int。所以最好使用代码点。从它们中创建一个字符串,如下所示:

int codePoint = ...
int[] codePoints = { codePoint };
String s = new String(codePoints, 0, codePoints.length);

那么整理就没问题了

顺便说一句,Character 有很多不错的 Unicode 信息:

String name = Character.getName(codePoint);