hibernate源码中AbstractEntityEntry类中的getCompressedValue方法

文章详细分析了Hibernate源码中AbstractEntityEntry类的一个方法,该方法使用位运算高效地存储和检索枚举状态,以节省内存。通过EnumState内部类,利用整数的位来压缩枚举值,结合位与和带符号右移运算符实现枚举状态的获取,展示了在性能优化和内存管理方面的巧妙设计。
摘要由CSDN通过智能技术生成

在查看hibernate源码,学习脏检查的操作时,我发现到这么一段代码,他出现在AbstractEntityEntry类中,这种编写方式,在我们平时的编程中几乎不曾使用过,也只有在源码中可以看到类似的写法。

package org.hibernate.engine.internal

***
public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
***
	/**
	 * Gets the current value of the given enum property.
	 *
	 * @param state
	 *            identifies the value to store
	 * @return the current value of the specified property
	 */
	protected <E extends Enum<E>> E getCompressedValue(EnumState<E> state) {
		// restore the numeric value from the bits at the right offset and return the corresponding enum constant
		final int index = ( ( compressedState & state.getMask() ) >> state.getOffset() ) - 1;
		return index == - 1 ? null : state.getEnumConstants()[index];
	}
}

该方法接受一个EnumState类型的参数(其中E是一个枚举类型),并返回一个和E相同类型的枚举类常量,我们看下EnumState是如何定义的,同样是在AbstractEntityEntry 类里,是一个静态的内部类,代码如下:

protected static class EnumState<E extends Enum<E>> {

		protected static final EnumState<LockMode> LOCK_MODE = new EnumState<LockMode>( 0, LockMode.class );
		protected static final EnumState<Status> STATUS = new EnumState<Status>( 4, Status.class );
		protected static final EnumState<Status> PREVIOUS_STATUS = new EnumState<Status>( 8, Status.class );

		protected final int offset;
		protected final E[] enumConstants;
		protected final int mask;
		protected final int unsetMask;

		private EnumState(int offset, Class<E> enumType) {
			final E[] enumConstants = enumType.getEnumConstants();

			// In case any of the enums cannot be stored in 4 bits anymore, we'd have to re-structure the compressed
			// state int
			if ( enumConstants.length > 15 ) {
				throw new AssertionFailure( "Cannot store enum type " + enumType.getName() + " in compressed state as"
						+ " it has too many values." );
			}

			this.offset = offset;
			this.enumConstants = enumConstants;

			// a mask for reading the four bits, starting at the right offset
			this.mask = 0xF << offset;

			// a mask for setting the four bits at the right offset to 0
			this.unsetMask = 0xFFFF & ~mask;
		}

		/**
		 * Returns the numeric value to be stored for the given enum value.
		 */
		private int getValue(E value) {
			return value != null ? value.ordinal() + 1 : 0;
		}

		/**
		 * Returns the offset within the number value at which this enum value is stored.
		 */
		private int getOffset() {
			return offset;
		}

		/**
		 * Returns the bit mask for reading this enum value from the number value storing it.
		 */
		private int getMask() {
			return mask;
		}

		/**
		 * Returns the bit mask for resetting this enum value from the number value storing it.
		 */
		private int getUnsetMask() {
			return unsetMask;
		}

		/**
		 * Returns the constants of the represented enum which is cached for performance reasons.
		 */
		private E[] getEnumConstants() {
			return enumConstants;
		}
	}

LOCK_MODE使用整型数值的第0到第3位(共4位)来存储其状态。
STATUS使用整型数值的第4到第7位(共4位)来存储其状态。
PREVIOUS_STATUS使用整型数值的第8到第11位(共4位)来存储其状态。
LockMode的枚举个数为12个,Status的枚举个数为6个,所以用4位长度来存储是肯定足够的。这种方法的缺点是代码可读性和易用性可能会受到一定影响,但在需要优化内存使用的场景中,这是一个很好的权衡。
this.mask = 0xF << offset;: mask 用于从整型数值中读取位偏移量处的 4 个位。0xF 是一个十六进制数字,它在二进制表示中等于 1111。通过将 0xF 左移 offset 位,创建了一个掩码,可以用来访问整型数值中位偏移量处的 4 个位。例如,如果 offset 为 4,则 mask 的二进制表示为 11110000。

this.unsetMask = 0xFFFF & ~mask;: unsetMask 用于将整型数值中位偏移量处的 4 个位设置为 0。首先,~mask 操作符计算 mask 的按位取反,将位偏移量处的 4 个位变为 0,其他位变为 1。然后,与 0xFFFF(16 位整数的最大值,二进制表示为 16 个 1)进行按位与,确保结果仍然是一个 16 位数值。这样,unsetMask 可以用来将整型数值中位偏移量处的 4 个位设置为 0,而保持其他位不变。

简而言之,mask 和 unsetMask 是两个掩码,分别用于访问和清除整型数值中指定位偏移量处的 4 个位。这在处理二进制数据时非常有用,例如在网络通信、数据压缩或内存优化等场景。

OK,再回头看getCompressedValue方法

protected <E extends Enum<E>> E getCompressedValue(EnumState<E> state) {
		// restore the numeric value from the bits at the right offset and return the corresponding enum constant
		final int index = ( ( compressedState & state.getMask() ) >> state.getOffset() ) - 1;
		return index == - 1 ? null : state.getEnumConstants()[index];
	}

&(位与运算符):对两个整数进行位与运算。只有当两个对应的位都为1时,结果的相应位才为1。例如,1101 & 1010 = 1000。

>>(带符号右移运算符):将整数的二进制表示向右移动指定的位数,左边空出的位用原数最高位的值填充(即正数补0,负数补1)。例如,1101 >> 2 = 0011。

  • 定义一个泛型方法<E extends Enum> E getCompressedValue(EnumState state),其中E是一个枚举类型。EnumState是一个泛型类,用于维护枚举类型的状态信息。

  • 使用位运算符&和>>从compressedState中提取特定的位。compressedState & state.getMask()表示将compressedState与state.getMask()进行位与运算,这会将compressedState中的某些位屏蔽掉,只保留我们关心的那些位。接下来的>> state.getOffset()表示将结果向右移state.getOffset()位,这样我们关心的位就移到了最低位。将结果保存到一个名为index的整数变量。

  • final int index = ( ( compressedState & state.getMask() ) >> state.getOffset() ) - 1;:根据提取到的位计算索引值。请注意,我们还需要减去1,因为在存储时,所有的枚举值的索引都加了1(这样做的原因是为了区分没有设置枚举值的情况,即index为-1)。

  • return index == -1 ? null : state.getEnumConstants()[index];:根据计算得到的index值返回相应的枚举常量。如果index等于-1,表示没有设置枚举值,因此返回null。否则,从state.getEnumConstants()数组中返回index位置的枚举常量。

总结:
在这段代码中,作者将枚举值的索引存储在一个整数(compressedState)中的特定位上。这意味着多个枚举状态可以共享一个整数,从而节省内存。同时,使用位操作(& 和 >>)可以在不使用额外空间的情况下高效地检索特定枚举状态。虽然这样的优化可能在小规模应用中看起来不明显,但在处理大量数据和对象时,这种节省会变得非常重要。编程有时候就是一种艺术!

本文部分内容由作者借助chatgpt生成引用,如果有误,还请指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值