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

以上代码没有全部涉及,但是具体的思想相差不多。~~~ over

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值