Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
仅通过文件之间的差异更新数据(delta for java)_Java_Delta - Fatal编程技术网

仅通过文件之间的差异更新数据(delta for java)

仅通过文件之间的差异更新数据(delta for java),java,delta,Java,Delta,更新:我用一个很棒的外部库解决了这个问题-。我这样做的方式作为公认的答案发布在下面 假设我有两台单独的PC,它们都有相同的字节[]A 其中一台PC创建字节[]B,该字节几乎与字节[]A相同,但为“较新”版本 要让第二台电脑将字节[]A的副本更新为最新版本(字节[]B),我需要将整个字节[]B传输到第二台电脑。如果字节[]B的大小超过GB,这将花费太长时间 是否可以创建一个字节[]C,它是字节[]a和字节[]B之间的“差异”?字节[]C的要求是,知道字节[]A,就可以创建字节[]B 这样,我只需要

更新:我用一个很棒的外部库解决了这个问题-。我这样做的方式作为公认的答案发布在下面

假设我有两台单独的PC,它们都有相同的字节[]A

其中一台PC创建字节[]B,该字节几乎与字节[]A相同,但为“较新”版本

要让第二台电脑将字节[]A的副本更新为最新版本(字节[]B),我需要将整个字节[]B传输到第二台电脑。如果字节[]B的大小超过GB,这将花费太长时间

是否可以创建一个字节[]C,它是字节[]a和字节[]B之间的“差异”?字节[]C的要求是,知道字节[]A,就可以创建字节[]B

这样,我只需要将字节[]C传输到第二台PC,从理论上讲,这只是字节[]B大小的一小部分

我正在寻找用Java解决这个问题的方法

非常感谢您提供的任何帮助:)

编辑:在大多数情况下,数据更新的本质是向数组的某些部分插入额外的字节。当然,可能会更改某些字节或删除某些字节。字节[]本身表示目标pc上所有文件/文件夹的名称树。字节[]最初是通过创建自定义对象树,使用JSON对其进行编组,然后使用zip算法压缩数据来创建的。我正在努力创建一个能够智能地创建对象c的算法


编辑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);