2

我有一个字节数组和一个对象引用。

    byte[] data = new byte[128];

    Block b = new Block();

我想将引用 b 存储在“数据”数组的最后 2 个(或 4 个)字节中。

请注意:我不想序列化对象并存储在字节数组中。我需要存储一个引用新块的指针(引用)。

编辑

我的 Block 类如下

    public class Block {
        byte[] data ;

        public Block(){
            data = new byte[128];
        }
}

基本上,数据数组将使用 126 字节来存储字符串,最后两个(或 4)字节来存储对另一个块的引用。它的一种链接列表。

我可以使用 Block 类的不同定义来完成它[通过在类本身中包含对 Block 的引用]。但是问题陈述指出了只有最后 2 个字节应该用作对另一个块的引用的约束。从其他帖子我知道在 jvm(32 位)中,引用的大小为 4 个字节。因此我认为只能使用最后 4 个字节来完成

问题陈述的片段

该块的最后 2 个字节用于指向下一个块。假设文件大小为 8 个块,那么第 4 个块的最后 2 个字节将指向第 5 个块,第 5 个块的最后 2 个字节指向第 6 个块,依此类推。

4

5 回答 5

5

基本上,数据数组将使用 126 字节来存储字符串,最后两个(或 4)字节来存储对另一个块的引用。它的一种链接列表。

您可以通过存储块索引来做到这一点。

例如

// list of blocks so you can lookup any block using a 16-bit index.
List<Block> blocks = new ArrayList<Block>();

int indexInBlocksToNext = ...
data[126] = (byte) (indexInBlocksToNext >> 8);
data[127] = (byte) indexInBlocksToNext;

我可以使用 Block 类的不同定义来完成它[通过在类本身中包含对 Block 的引用]。但是问题陈述指出了只有最后 2 个字节应该用作对另一个块的引用的约束。从其他帖子我知道在 jvm(32 位)中,引用的大小为 4 个字节。因此我认为只能使用最后 4 个字节来完成

所有 32 位系统都使用 32 位指针或引用。您不能在 Java 中放置引用,因为没有通过数字引用对象的全局方式。您可以获取对象在内存中的位置,但此位置可以随时更改。


在 Java 7 中,开始之前使用的最小内存约为 1.3 MB。

$ java -mx1200k -version
Error occurred during initialization of VM
Too small initial heap for new size specified
$ java -mx1300k -version
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)

这意味着您甚至在程序开始之前就已经使用了超过 1 MB 的预算。

于 2012-09-16T13:36:47.810 回答
1

您可以通过序列化任何对象ObjectOutputStream(请注意,您必须添加implements Serializable到您的类Block)。例如:

Block block = new Block();
// ...
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(new File("test.dat"))));
oos.wrtieObject(block);
oos.close();

然后像这样阅读:

ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File("test.dat"))));
Block block = (Block) ois.readObject();
ois.close();
于 2012-09-16T13:17:16.570 回答
1

我需要存储一个引用新块的指针(引用)。

基本上,在纯 Java 中根本无法做到这一点。

执行此操作的非纯 Java 方法(使用 JNI 或Unsafe类)将为您提供一个无法安全地转回 Java 引用的值。

原因是当 GC 运行时,它通常会将可达对象移动到新位置。现在,如果您在正确的引用类型字段、变量或数组槽中有对对象的引用,GC 将找到该引用副本并将其更新为指向对象的新位置。但是如果引用已经变成了字节或其他东西,那么 GC 将不知道字节实际上是引用的表示,并且不会更新它。因此,您的引用表示为字节现在将指向错误的位置。如果您将其转回参考并尝试使用它,则可能会出现混乱。


您需要找到一种不同的方式来表示这些Block引用。如果您不愿意自己序列化Block对象,那么显而易见的选项是索引或某种类型的 Map 键。在所有情况下,您的数据结构都需要保存垃圾收集器可访问的对象/数组中的真实引用。

于 2012-09-16T14:01:27.793 回答
0

我认为你可以做的是:

  1. 第一个序列化块类。
  2. 然后使用类似下面的东西将块对象转换为字节数组。

` public byte[] toByteArray(Object obj) {//

    byte[] bytes = null;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try {
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        oos.flush();
        oos.close();
        bos.close();
        bytes = bos.toByteArray();
    } catch (IOException ex) {
        System.out.println(ex);
    }
    return bytes;

`

  1. 使用System.arraycopy (src, srcPos, dest, destPos, length) 方法从源对象复制到目标对象。
于 2012-09-16T13:36:03.077 回答
0

对于 java 中的内存数据结构,这是无法做到的。

jvm 规范故意模糊对象引用的存储和分配方式,以允许不同的 jvm 实现对它们如何实现垃圾收集等做出不同的选择。

您可以做的最好的事情(如果在内存中工作)是模拟具有大字节数组的地址空间,您使用数组中的索引作为指针。

如果在磁盘上工作,那就是另一回事了,因为基于磁盘的存储可以通过偏移量访问,并且指针是有意义的,但是您的内存数据结构变得无关紧要

于 2012-09-16T14:03:52.060 回答