在 Java 中实现 C 风格的位域
Posted
技术标签:
【中文标题】在 Java 中实现 C 风格的位域【英文标题】:Implementing a C style bitfield in Java 【发布时间】:2012-09-14 19:05:48 【问题描述】:我有一个问题,我有点卡住了,一位同事告诉我,这是寻求帮助的好地方。
我正在尝试。 这是一个粗略的示例(此时我面前没有实际的代码)。
typedef union
typedef struct
unsigned short a :1;
unsigned short b :1;
unsigned short c :2;
unsigned short d :10;
bitfield;
unsigned short bitmap;
example_bitfield;
我有很多来自遗留代码的类似风格的位域。我需要为 Java 提出一种等效方法的原因是,我正在编写将使用 Java 与使用 UDP 的其他遗留应用程序进行通信的代码。
我没有重写代码的选项。我知道这种方法不可移植,存在字节顺序问题(以及填充/对齐等),如果我能够重写代码,可以做得更好。不幸的是,我需要这个非常具体的问题的答案。系统是封闭的,所以我不需要担心编译器/操作系统/等的每一个可能的组合。
使用 Java EnumSet 的方法行不通,因为我相信这只会允许每个值是一位。我需要能够使用例如 d 占用 10 位的值来打包值。
我知道 Java Bitset,但它有局限性。我使用的是较旧版本的 Java,因此我没有一些较新的 Java Bitset 方法(即 valueOf 方法,可能肯定会有所帮助)。
有没有人知道如何尽可能地管理它?我有超过 10 个位域需要为我的通信实现。
感谢您提供的任何帮助!
【问题讨论】:
请注意,您的原始示例实际上是未定义的行为。 你有一个旧的和有限的 Java 版本,你能告诉我们它是什么吗? 它是 Java SE 6。从技术上讲,位域是使用 c++ 编译器编译的。我相信 c++ 添加了对使用整数以外的类型的支持。如果它是未定义的,我可以接受……我没有更正它的选项,而且它当前正在做的任何行为都是我必须效仿的。 【参考方案1】:由于 UDP 只接受字节数组,您可以以任何合适的方式声明 Java 类,唯一关键的步骤是定义其序列化和反序列化方法:
class example_bitfield
byte a;
byte b;
byte c;
short d;
public void fromArray(byte[] m)
byte b0=m[0];
byte b1=m[1];
a=b0>>>7;
b=(b0>>6)&1;
c=(b0>>4)&3;
d=(b0&0xF<<6)|(b1>>>2);
public void toArray(byte[] m)
m[0]=(a<<7)|(b<<6)|(c<<4)|(d>>>6);
m[1]=(d&0x3F)<<2;
【讨论】:
好的,感谢您的反馈。我仍然希望有一个更通用的解决方案,因为我有很多位域要处理。手动执行每个操作似乎都是非常痛苦的经历。 注意不要将任何成员值设置为超出其有效范围,或者在序列化时添加一些掩码;现在,序列化a = 2
将导致a = 0
、b = 1
。【参考方案2】:
来自 Javolution 库的类结构可满足您的需求 (http://www.javolution.org/apidocs/index.html?javolution/io/Struct.html) 参见“时钟”示例:
import java.nio.ByteBuffer;
class Clock extends Struct // Hardware clock mapped to memory.
Unsigned16 seconds = new Unsigned16(5); // unsigned short seconds:5
Unsigned16 minutes = new Unsigned16(5); // unsigned short minutes:5
Unsigned16 hours = new Unsigned16(4); // unsigned short hours:4
Clock()
setByteBuffer(Clock.nativeBuffer(), 0);
private static native ByteBuffer nativeBuffer();
【讨论】:
【参考方案3】:我最终使用了此处介绍的类似方法:What is the most efficent way in Java to pack bits
然后我创建了一个包装类,它使用LinkedHashMap 来存储各个位字段条目。
每个字段都被实现为一个存储位数和字段值的类。字段名称是 LinkedHashMap 的键。
我添加了用于开始和结束结构的方法、向结构添加位字段的方法以及基于键获取和设置值的方法。
我的 pack 方法遍历 LinkedHashMap 并放置位,同时跟踪位偏移量(为此我只使用了一个整数)。
unpack 方法还迭代 LinkedHashMap 并获取位,跟踪位偏移,并将值存储在 LinkedHashMap 中。
为方便起见,我编写了将位字段打包为整数、短整数、长整数和字节的方法。为了在字节数组和值之间进行转换,我使用了 ByteBuffer 并调用了 wrap 方法。
我还编写了解包压缩整数、短整数、长整数或字节的方法,方法是首先为数据类型所具有的字节数分配 ByteBuffer(整数为 4,短整数为 2,等等),然后调用各种ByteBuffer 的 put 方法。一旦我有了一个字节数组,我就可以将它传递给 unpack 方法。
我采用这种方法是因为我需要一些自包含的东西,易于使用,而且其他人也很容易遵循......我知道可能有更优雅的方式涉及注释或其他东西(我找到了 JavaStruct,但它没有包含位字段。)
从各种原始数据类型打包和解包使我能够更轻松地从 DataInputStream/DataOutputStream 读取和写入结果。
很抱歉,我无法发布代码让大家受益,所以上面的解释就足够了。希望它会帮助处于类似情况的人:)。
【讨论】:
【参考方案4】:一些粗略的搜索并没有发现任何库来简化此操作,但您始终可以通过按位运算手动打包和解包:
class ExampleBitfield
int bitfield; // actually 16 bits
public int getBitfield()
return bitfield;
public void setBitfield(int bitfield)
this.bitfield = bitfield & 0xffff;
// lowest bit
public int getA()
return (bitfield >> 0) & 0x01;
public int setA(int a)
return (bitfield & ~0x01) | ((a & 0x01) << 0);
// second lowest bit
public int getB()
return (bitfield >> 1) & 0x01;
public int setB(int b)
return (bitfield & ~0x02) | ((b & 0x01) << 1);
// ...
【讨论】:
谢谢你,willglynn。我知道这是可能的,但是考虑到我必须处理的位域数量,我担心这种方法很难管理并且可能难以调试。我发现了这个 link 这似乎与您的方法相似,但可能更通用。以上是关于在 Java 中实现 C 风格的位域的主要内容,如果未能解决你的问题,请参考以下文章