优化的依据是什么?
Java 中的 char
是两个byte
大小,因为我们大多数的时候操作数据都是都是用拉丁语系的字符的,而拉丁语系的字符只要用byte
就足够存储了,根本就不需要char
。所以如果我们发现发现了一个字符串里只有拉丁语系的字符,那么我们全都用byte
,这样就比原来的用char
来存储节省一半的存储空间了。
具体实现思想是什么?
判断一个字符串里是否都是拉丁语系的字符,如果全都是,那么OK,一个char
用一个byte
来代替就行,存储就是简单的一个直接截取char
的起始八位就行。
代码实现
String 中的代码:
- 先判断是否开启了字段压缩机制是否开启,默认是开启的
- 如果开启了,就用
StringUTF16.compress(value, off, len);
来压缩,需要判断是否压缩成功,因为我们无法事先知道字符串里是否都是拉丁语系的字符组成的。 - 如果没有开启,那就用
StringUTF16.toBytes(value, off, len);
就是默认都用utf-16
来处理,这样其实就没有达到节省空间的目的了。
代码解读
复制代码
static final boolean COMPACT_STRINGS;
static {
COMPACT_STRINGS = true;
}
public String(char value[]) {
this(value, 0, value.length, null);
}
String(char[] value, int off, int len, Void sig) {
if (len == 0) {
this.value = "".value;
this.coder = "".coder;
return;
}
if (COMPACT_STRINGS) {
byte[] val = StringUTF16.compress(value, off, len);
if (val != null) {
this.value = val;
this.coder = LATIN1;
return;
}
}
this.coder = UTF16;
this.value = StringUTF16.toBytes(value, off, len);
}
再来看看几个关键的方法:compress
, toBytes
。
compress
方法,逐个遍历字符,如发现字符的对应的数字大于0xFF
,那么就退出,毫无疑问,就代表着这个不是拉丁字符系的,那么就退出了,且返回0; 如果都是拉丁语系的,那么就都用byte
来保存toBytes
就是代表着用两个byte
来保存char
数据了,就没有起到节省空间的目的。
代码解读
复制代码
private static native boolean isBigEndian();
static final int HI_BYTE_SHIFT;
static final int LO_BYTE_SHIFT;
static {
if (isBigEndian()) {
HI_BYTE_SHIFT = 8;
LO_BYTE_SHIFT = 0;
} else {
HI_BYTE_SHIFT = 0;
LO_BYTE_SHIFT = 8;
}
}
// 如果返回的长度不等于len,那么就要返回null,就说明有非拉丁语系的字符存在。
public static byte[] compress(char[] val, int off, int len) {
byte[] ret = new byte[len];
if (compress(val, off, ret, 0, len) == len) {
return ret;
}
return null;
}
// compressedCopy char[] -> byte[]
//逐个遍历字符,如发现字符的对应的数字大于`0xFF`,那么就退出,毫无疑问,就代表着这个不是拉丁字符系的,那么就退出了。
@HotSpotIntrinsicCandidate
public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
for (int i = 0; i < len; i++) {
char c = src[srcOff];
if (c > 0xFF) {
len = 0;
break;
}
dst[dstOff] = (byte)c;
srcOff++;
dstOff++;
}
return len;
}
// 其实如果用了这个方法,其实就达不到节省空间的目的了,也就意味着,其中有个非拉丁字符,
// 每个字符都要转成两个byte来存储。
@HotSpotIntrinsicCandidate
public static byte[] toBytes(char[] value, int off, int len) {
byte[] val = newBytesFor(len);
for (int i = 0; i < len; i++) {
putChar(val, i, value[off]);
off++;
}
return val;
}
@HotSpotIntrinsicCandidate
// intrinsic performs no bounds checks
static void putChar(byte[] val, int index, int c) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;
// 移位,然后直接硬转,直接截取低八位。
val[index++] = (byte)(c >> HI_BYTE_SHIFT);
val[index] = (byte)(c >> LO_BYTE_SHIFT);
}