JAVA的String的实现

一。Unicode和UTF-16

要说String必然跑不掉Unicode。简单说Unicode是一种字符集,说白了就是每个字符分配了一个数字与之对应。其范围是U+0000到U+10FFFF。

再来说两个概念叫代码点(Code Point)和代码单元(Code Unit)。

代码点:指的就是Unicode的中每个字符对应的数值,范围在U+0000到U+10FFFF之间。

代码单元:就是用来表示unicode的数值时采用的编码的最小单位,UTF-8就是8位即一个字节,UTF-16就是2个字节,UTF-32就是4个字节。

UTF-8的代码单元是一个字节,要存储Unicode就必须采用一定编码方式即

000000 - 00007F 0zzzzzzz(00-7F) 1个字节
000080 - 0007FF 110yyyyy(C0-DF) 10zzzzzz(80-BF) 2个字节
000800 -  00FFFF 1110xxxx(E0-EF) 10yyyyyy 10zzzzzz 3个字节
010000 - 10FFFF 11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz 4个字节

UTF-16的代码单元是两个字节,编码方式

000000 -  00FFFF xxxxxxxx xxxxxxxx yyyyyyyy yyyyyyyy 2个字节
010000 - 10FFFF 110110yyyyyyyyyy 110111xxxxxxxxxx 4个字节


二。String内部的实现。

String中实际数据的存储就是Unicode的UTF-16的编码形式。所以代码单元是2个字节,这也符合char的大小。现在有个很明显的问题就是Unicode的范围是U+0000到U+10FFFF,而一个char没法表示U+10000到U+10FFFF之前的数值,为了解决这个问题就采用了上面介绍的两个char来组合表示这个范围的值。

分拆的规则是10000到10FFFF,按照最大值算一共占了20位,高10位的值加上0xD800后存在一个char中,低10位的值加上0xDC00存在一个char中,这样两个char组合起来表示一个超过10000的unicode编码值。即 U+0000到U+FFFF用一个char表示,这部分叫基本多语言平面(BMP Basic Multilingual Plane)。ps:绕口得绕死了。U+10000到U+10FFFF用两个char表示,这部分叫辅助平面(Supplementary Planes)。

看似上面解释的很好,其实还有一个问题。当遇到一个2个char组成的Unicode编码时,怎么知道何时解析成一个,何时解析成两个单独的Unicode编码。其实就是说 如果存在一个char的和两个char在表示的时候 如果存在重复前缀的时候该怎么区分出来?例如如果一个char值是 '\uD869',两个char的是'\uD869'和'\uDEA5',就会出现无法唯一解析的问题,是把'\uD869'和'\uDEA5'当成两个单独的unicode来解析呢?还是当成一个整体的unicode来解析呢?其实这个问题unicode在制定编码的时候就考虑了,解决方案就是Unicode标准规定U+D800到U+DFFF的值不对应于任何字符。这样只要遇到在U+D800和U+DFFF之间的char,就知道下一个char跟这个是一体的,不能分别拆开解析,必须两个char一起解析。


知道上面这些后其实对于String的底层存储就比较容易理解了。String内部是定义了几个主要的基本数据类型。

private final char value[]; //存储unicode值,不过是以UTF-16编码的方式存储。

private final int offset; //对于第一个字符与数组的偏移量。

private final int count; //String中代码点的个数。

private int hash; //缓存Hash值


你可能感兴趣的:(JAVA的String的实现)