仅通过文件之间的差异更新数据(delta for java)
更新:我用一个很棒的外部库解决了这个问题-。我这样做的方式作为公认的答案发布在下面 假设我有两台单独的PC,它们都有相同的字节[]A 其中一台PC创建字节[]B,该字节几乎与字节[]A相同,但为“较新”版本 要让第二台电脑将字节[]A的副本更新为最新版本(字节[]B),我需要将整个字节[]B传输到第二台电脑。如果字节[]B的大小超过GB,这将花费太长时间 是否可以创建一个字节[]C,它是字节[]a和字节[]B之间的“差异”?字节[]C的要求是,知道字节[]A,就可以创建字节[]B 这样,我只需要将字节[]C传输到第二台PC,从理论上讲,这只是字节[]B大小的一小部分 我正在寻找用Java解决这个问题的方法 非常感谢您提供的任何帮助:) 编辑:在大多数情况下,数据更新的本质是向数组的某些部分插入额外的字节。当然,可能会更改某些字节或删除某些字节。字节[]本身表示目标pc上所有文件/文件夹的名称树。字节[]最初是通过创建自定义对象树,使用JSON对其进行编组,然后使用zip算法压缩数据来创建的。我正在努力创建一个能够智能地创建对象c的算法仅通过文件之间的差异更新数据(delta for java),java,delta,Java,Delta,更新:我用一个很棒的外部库解决了这个问题-。我这样做的方式作为公认的答案发布在下面 假设我有两台单独的PC,它们都有相同的字节[]A 其中一台PC创建字节[]B,该字节几乎与字节[]A相同,但为“较新”版本 要让第二台电脑将字节[]A的副本更新为最新版本(字节[]B),我需要将整个字节[]B传输到第二台电脑。如果字节[]B的大小超过GB,这将花费太长时间 是否可以创建一个字节[]C,它是字节[]a和字节[]B之间的“差异”?字节[]C的要求是,知道字节[]A,就可以创建字节[]B 这样,我只需要
编辑2:非常感谢大家给我的帮助,我很抱歉这么长时间不活跃。我很可能会尝试使用一个外部库来为我进行增量编码。关于这条线索的一个重要部分是,我现在知道我想要实现的是所谓的!我相信,当我找到一个合适的解决方案时,我会发布并接受它,这样其他人就可以看到我是如何解决我的问题的。再次感谢您的帮助。本地记录对字节数组的更改,就像一个小型版本控制系统一样。事实上,您可以使用VCS创建补丁文件,将它们发送到另一端并应用它们以获得最新文件 如果无法记录更改,则需要在本地将数组加倍,或者(并非100%安全)在块上使用校验和数组。使用“更改事件”集合,而不是发送整个数组 解决方案是发送一个序列化对象来描述更改,而不是重新发送实际数组
public class ChangePair implements Serializable{
//glorified struct
public final int index;
public final byte newValue;
public ChangePair(int index, byte newValue) {
this.index = index;
this.newValue = newValue;
}
public static void main(String[] args){
Collection<ChangePair> changes=new HashSet<ChangePair>();
changes.add(new ChangePair(12,(byte)2));
changes.add(new ChangePair(1206,(byte)3));
}
}
公共类变更对实现可序列化{
//美化结构
公共最终综合指数;
公共最终字节值;
公共变更对(int索引,字节newValue){
这个指数=指数;
this.newValue=newValue;
}
公共静态void main(字符串[]args){
集合更改=新的HashSet();
添加(新的变更对(12,(字节)2));
添加(新的变更对(1206,(字节)3));
}
}
生成“更改事件”
实现这一点的最有效方法是在进行过程中跟踪更改,但假设这不可能,您可以通过蛮力强行通过,找到不同的值
public static Collection<ChangePair> generateChangeCollection(byte[] oldValues, byte[] newValues){
//validation
if (oldValues.length!=newValues.length){
throw new RuntimeException("new and old arrays are differing lengths");
}
Collection<ChangePair> changes=new HashSet<ChangePair>();
for(int i=0;i<oldValues.length;i++){
if (oldValues[i]!=newValues[i]){
//generate a change event
changes.add(new ChangePair(i,newValues[i]));
}
}
return changes;
}
public静态集合generateChangeCollection(字节[]oldValues,字节[]newValues){
//验证
if(oldValues.length!=newValues.length){
抛出新的RuntimeException(“新数组和旧数组的长度不同”);
}
集合更改=新的HashSet();
对于(inti=0;i而言,这里的主要问题是数据压缩
为数据数组提供良好的压缩算法。它使用。Simple16是一个很好的且(顾名思义)简单的列表压缩选项。或者您可以使用。或者您可以使用Java中可用的任何压缩算法进行实验
无论如何,如果您首先对数据进行预处理,您使用的任何方法都将得到优化
您可以减少数据计算差异,或者如@richardingle所指出的,创建不同数据位置的对
您可以将C
计算为B
-A
A
必须是int
数组,因为两个byte
值之间的差值可能高于255
。然后可以将B
还原为A
+C
在这里至少结合两种方法的优点是可以得到更好的结果
例如,如果对A={1,2,3,4,5,6,7}
和B={1,2,3,5,6,7,7}
使用差分方法,则差分数组C
将是{0,0,0,1,1,1,0}
.RLE可以以非常有效的方式压缩C
,因为它有助于在序列中有许多重复的数字时压缩数据
如果您的数据几乎在每个位置都发生了变化,那么在Simple16中使用差分方法会很好,但值之间的差异很小。它可以将28个单位值(0
或1
)的数组或14个双位值的数组压缩为单个32字节整数
实验,这一切都取决于你的数据表现如何。并比较每个实验的数据压缩率
编辑:在进行JSON和zip压缩之前,必须对数据进行预处理
创建两组old
和now
。后者包含现在存在的所有文件。对于前者,即旧文件,您至少有两个选项:
- 应包含在您将文件发送到另一台电脑之前存在的所有文件。您需要保留另一台电脑知道的一组内容,以计算自上次同步以来发生的更改,并仅发送新数据
- 包含自上次检查更改以来的所有文件。您可以保留更改的本地历史记录,并为每个版本提供一个“id”。然后,同步时,将“版本id”与
Collection<ChangePair> changes=generateChangeCollection(oldValues,newValues);
Socket s = new Socket("yourhostname", 1234);
ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream());
out.writeObject(objectToSend);
out.flush();
ServerSocket server = new ServerSocket(1234);
Socket s = server.accept();
ObjectInputStream in = new ObjectInputStream(s.getInputStream());
Collection<ChangePair> objectReceived = (Collection<ChangePair>) in.readObject();
//use Collection<ChangePair> to apply changes
public static void useChangeCollection(byte[] oldValues, Collection<ChangePair> changeEvents){
for(ChangePair changePair:changeEvents){
oldValues[changePair.index]=changePair.newValue;
}
}
//Create delta
String[] deltaArgs = new String[]{fileAJson.getAbsolutePath(), fileBJson.getAbsolutePath(), fileDelta.getAbsolutePath()};
XDeltaEncoder.main(deltaArgs);
//Apply delta
deltaArgs = new String[]{"-d", fileAJson.getAbsolutePath(), fileDelta.getAbsolutePath(), fileBTarget.getAbsolutePath()};
XDeltaEncoder.main(deltaArgs);
//Trivia, Surpisingly this also works
deltaArgs = new String[]{"-d", fileBJson.getAbsolutePath(), fileDelta.getAbsolutePath(), fileBTarget.getAbsolutePath()};
XDeltaEncoder.main(deltaArgs);