Java char[] 到 byte[] 背后的 11 个压缩字符串魔法

Java 11 Compact Strings magic behind char[] to byte[]

最近两天我一直在阅读有关编码 Unicode Java 9 compact Strings 的文章,我感觉很好。但是有一点我不明白。

关于字节数据类型

1).是一个8位存储范围从-128到127

问题

1).为什么 Java 没有像 char unsigned 16 bits 那样实现?我的意思是它会在 0.256 的范围内,因为从 0 到 127 我只能保存一个 Ascii 值,但是如果我将值设置为 200 会发生什么,扩展的 ascii 会溢出到 -56。

2).负值是否意味着什么我的意思是我已经尝试使用 Java 11

的简单示例
final char value = (char)200;//in byte would overflow
final String stringValue = new String(new char[]{value});
System.out.println(stringValue);//THE SAME VALUE OF JAVA 8

我检查了 String.value 变量,我看到一个

的字节数组
System.out.println(value[0]);//-56

出现与之前相同的问题 -56 是否意味着我的意思(负值)在其他语言中这个溢出被检测到 return 到值 200? Java 怎么知道 -56 值和 char.

中的 200 是一样的

我尝试了最困难的示例,例如代码点 128048,我在 String.value 变量中看到了这样的字节数组。

0 = 61 
1 = -40
2 = 48
3 = -36

我知道这个代码点需要 4 个字节,但我知道它是如何将 char[] 转换为 byte[] 但我不知道 String 如何处理这个 byte[] 数据。

很抱歉,如果这个问题很简单,很抱歉打字英语不是我的母语,非常感谢。

这是因为并非字符串中的所有字节都被解释为相同的。这取决于字符串的 character encoding.

示例:

  • 如果字符串是 UTF-8 字符串,其字符大小为 8 位。
  • 在 UTF-16 字符串中,其字符大小为 16 位。
  • 等...

这意味着,如果要将字符串表示为UTF-8,则字符将通过一次读取1个字节来生成;如果是 16 位,字符将通过一次读取 2 个字节来组成。

看这段代码:使用 UTF-8 和 UTF-16 将单字节数组 data 转换为字符串。

byte[] data = new byte[] {97, 98, 99, 100};
System.out.println(new String(data, StandardCharsets.UTF_8));
System.out.println(new String(data, StandardCharsets.UTF_16));

这段代码的输出是:

abcd // 4 bytes = 4 chars, 1 byte per char
慢捤  // 4 bytes = 2 chars, 2 byte per char

回到问题,开发人员这样做的动机是减少字符串的内存占用。并非所有字符串都使用 char 提供的所有 16 位。

编辑:Code here

Why Java didn't implement it like char unsigned 16 bits? i mean it would be in a range of 0.256 because from 0 to 127 only can i hold a Ascii value but what would happen if i set the value 200 a extended ascii would overflow to -56.

Java 的原始数据类型在四分之一个世纪前由 Java 1.0 解决。压缩字符串是在不到两年前的 Java 9 中引入的。这个新功能只是一个实现细节,并不能证明 Java 类型系统的根本变化是合理的。

除此之外,您正在查看存储在一个字节中的数据的一个解释。为了表示 iso-latin-1 单位,将相同的数据解释为 Java 的内置符号 byte 会导致正数还是负数完全无关紧要。

同样 Java 的 I/O API 允许将文件读入 byte[] 数组并将 byte[] 数组写回文件和这两个操作已经足以无损地复制文件,无论其文件格式如何在解释其内容时都是相关的。

因此自 Java 1.1 起以下内容有效:

byte[] bytes = "È".getBytes("iso-8859-1");
System.out.println(bytes[0]);
System.out.println(bytes[0] & 0xff);
-56
200

-56200 这两个数字只是对位模式 11001000 的不同解释,而 byte 的 iso-latin-1 解释包含位模式 11001000 是字符 È.

A char 值也只是对两个字节数量的解释,即作为 UTF-16 代码单元。同样,char[] 数组是计算机内存中具有标准解释的字节序列。

我们也可以这样解释其他字节序列。

StringBuilder sb = new StringBuilder().appendCodePoint(128048);
byte[] array = new byte[4];
StandardCharsets.UTF_16LE.newEncoder()
    .encode(CharBuffer.wrap(sb), ByteBuffer.wrap(array), true);
System.out.println(Arrays.toString(array));

将打印您看到的值,[61, -40, 48, -36]

String class 中使用 byte[] 数组的好处是,现在可以选择解释,当所有字符都是 iso-latin-1 时可以使用此编码或 utf-16 表示。

可能的数字解释与字符串无关。然而,当你问“Java如何知道-56值与200相同”时,你应该问自己,它如何知道byte的位模式11001000 -56排在首位吗?

System.out.println(value[0]);
与普通计算机算术相比,

承担了一项实际昂贵的操作,将 byte(或 int)转换为 String。这种转换操作经常被忽视,因为它被定义为打印 byte 的默认方式,但并不比将值解释为无符号数量的 String 转换更自然。如需进一步阅读,我推荐 Two's complement.