Java 11 的String是如何优化存储的?

程序浅谈 后端 2024-12-10

Java 11 的String是如何优化存储的?

优化的依据是什么?

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

再来看看几个关键的方法:compresstoBytes

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

转载来源:https://juejin.cn/post/6844904002325331981

Apipost 私有化火热进行中

评论