草庐IT

java:为自定义序列化分配对象引用 ID

coder 2024-03-31 原文

出于各种原因,我有一个自定义序列化,我将一些相当简单的对象转储到数据文件中。可能有 5-10 个类,生成的对象图是非循环的并且非常简单(每个序列化对象都有 1 或 2 个对另一个序列化对象的引用)。例如:

class Foo
{
    final private long id;
    public Foo(long id, /* other stuff */) { ... }
}

class Bar
{
    final private long id;
    final private Foo foo;
    public Bar(long id, Foo foo, /* other stuff */) { ... }
}

class Baz
{
    final private long id;
    final private List<Bar> barList;
    public Baz(long id, List<Bar> barList, /* other stuff */) { ... }
}

id字段只是为了序列化,所以当我序列化到一个文件时,我可以通过记录到目前为止已经序列化了哪些ID来编写对象,然后为每个对象检查它的子对象是否已经被序列化序列化并写入那些没有序列化的,最后通过写入其数据字段和与其子对象对应的 ID 来写入对象本身。

令我困惑的是如何分配 id。想了想,分配ID好像有3种情况:

  • 动态创建的对象——id 是从递增的计数器分配的
  • 从磁盘读取对象 -- id 从存储在磁盘文件中的数字分配
  • 单例对象 -- 对象先于任何动态创建的对象创建,以表示始终存在的单例对象。

我该如何正确处理这些问题?我觉得我正在重新发明轮子,并且必须有一种完善的技术来处理所有情况。


澄清:正如一些无关紧要的信息,我正在查看的文件格式大致如下(掩盖了一些不相关的细节)。它经过优化,可以处理相当大量的密集二进制数据(数十/数百 MB),并能够在其中散布结构化数据。密集的二进制数据占文件大小的 99.9%。

该文件由一系列作为容器的纠错 block 组成。每个 block 都可以被认为包含一个由一系列数据包组成的字节数组。可以一次一个地连续读取数据包(例如,可以判断每个数据包的结尾在哪里,然后下一个数据包立即开始)。

所以文件可以被认为是存储在纠错层之上的一系列数据包。这些数据包中的绝大多数是不透明的二进制数据,与本题无关。然而,这些数据包中的一小部分是包含序列化结构化数据的项目,形成了一种由数据“岛”组成的“群岛”,这些“岛”可以通过对象引用关系链接起来。

所以我可能有一个文件,其中数据包 2971 包含一个序列化的 Foo,数据包 12083 包含一个序列化的 Bar,它引用数据包 2971 中的 Foo。(数据包 0-2970 和 2972​​-12082 是不透明数据包)

所有这些数据包都是不可变的(因此考虑到 Java 对象构造的约束,它们形成了一个非循环对象图)所以我不必处理可变性问题。它们也是通用 Item 接口(interface)的后代。我想做的是将任意 Item 对象写入文件。如果 Item 包含对文件中已有的其他 Item 的引用,我也需要将它们写入文件,但前提是它们尚未写入.否则,当我读回它们时,我将需要以某种方式合并重复项。

最佳答案

你真的需要这样做吗?在内部,ObjectOutputStream 跟踪哪些对象已经被序列化。同一对象的后续写入仅存储内部引用(类似于只写出 id)而不是再次写出整个对象。

参见 Serialization Cache了解更多详情。

如果 ID 对应于某些外部定义的身份,例如实体 ID,那么这是有道理的。但问题指出,生成 ID 纯粹是为了跟踪哪些对象被序列化。

您可以通过 readResolve 方法处理单例。一种简单的方法是将新反序列化的实例与您的单例实例进行比较,如果匹配,则返回单例实例而不是反序列化的实例。例如

   private Object readResolve() {
      return (this.equals(SINGLETON)) ? SINGLETON : this;
      // or simply
      // return SINGLETON;
   }

编辑:作为对评论的回应,流主要是二进制数据(以优化格式存储),复杂对象分散在该数据中。这可以通过使用支持子流的流格式来处理,例如zip,或简单的 block 分块。例如。流可以是一系列 block :

offset 0  - block type
offset 4  - block length N
offset 8  - N bytes of data
...
offset N+8  start of next block

然后您可以拥有用于二进制数据的 block 、用于序列化数据的 block 、用于 XStream 序列化数据的 block 等。由于每个 block 都知道它的大小,因此您可以创建一个子流以从文件中的位置读取该长度。这使您可以自由混合数据而无需担心解析。

要实现一个流,让你的主流解析 block ,例如

   DataInputStream main = new DataInputStream(input);
   int blockType = main.readInt();
   int blockLength = main.readInt();
   // next N bytes are the data
   LimitInputStream data = new LimitInputStream(main, blockLength);

   if (blockType==BINARY) {
      handleBinaryBlock(new DataInputStream(data));
   }
   else if (blockType==OBJECTSTREAM) {
      deserialize(new ObjectInputStream(data));
   }
   else
      ...

LimitInputStream 的草图如下所示:

public class LimitInputStream extends FilterInputStream
{
   private int bytesRead;
   private int limit;
   /** Reads up to limit bytes from in */
   public LimitInputStream(InputStream in, int limit) {
      super(in);
      this.limit = limit;
   }

   public int read(byte[] data, int offs, int len) throws IOException {
      if (len==0) return 0; // read() contract mandates this
      if (bytesRead==limit)
         return -1;
      int toRead = Math.min(limit-bytesRead, len);
      int actuallyRead = super.read(data, offs, toRead);
      if (actuallyRead==-1)
          throw new UnexpectedEOFException();
      bytesRead += actuallyRead;
      return actuallyRead;
   }

   // similarly for the other read() methods

   // don't propagate to underlying stream
   public void close() { }
}

关于java:为自定义序列化分配对象引用 ID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2998100/

有关java:为自定义序列化分配对象引用 ID的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  3. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  7. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  10. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

随机推荐