2つのバイト配列を比較する

週末に、「与えられた2つのバイト配列を比較し、一致した要素と、一致しなかった要素を取得する」プログラムを(何も考えずに)書いていたら、、はまった(あー、ほんとダメだ)。
次の2つのバイト配列が比較できなかったのだ。。

    byte[] a = { 0x00, 0x11, 0x22, 0x22 };
    byte[] b = { 0x00, 0x22, 0x22, 0x33 };

これらを比較した結果、「1番目と3番目の要素が一致、2番目と4番目の要素が不一致」とすることもできるし、「バイト配列aの2番目の要素が余計で、バイト配列bの4番目の要素が余計。0x00、0x22、0x22の3つの要素が一致」とすることもできる。
比較する配列の特性や、取得したい結果によって、比較方法を変えなければならないような気がした。


そこで、1バイトずつ比較するのか、4バイト先まで比較する(一致した要素があった場合には、そこまでの要素を、余計な要素とする)のか、比較する範囲を指定できるようにした(せざるを得なかった)。
先ほど、ビール片手に、次のBytesDiffクラスを作成した。

public class BytesDiff {

    private byte[] base;
    private byte[] target;

    private final ArrayList<Byte> matchedBytes = new ArrayList<Byte>();
    private final Map<Integer, Byte> baseOnlyBytes = new HashMap<Integer, Byte>();
    private final Map<Integer, Byte> targetOnlyBytes = new HashMap<Integer, Byte>();

    /**
     * @param base 比較元となるバイト配列
     * @param target 比較先となるバイト配列
     */
    public BytesDiff(byte[] base, byte[] target) {
        this.base = base;
        this.target = target;
    }

    public void diff(int diffRange) {
        if (diffRange < 0) throw new IllegalArgumentException(
                String.format("比較する範囲として、負の数 %s が指定されました。", diffRange));
        clearResult();

        int bi = 0;  // 比較元バイト配列のインデックス
        int ti = 0;  // 比較先バイト配列のインデックス

        while (bi < base.length && ti < target.length) {

            boolean matched = false;

            for (int offset = 0;
                     offset < diffRange && bi + offset < base.length && ti + offset < target.length;
                     offset++) {

                if (base[bi] == target[ti + offset]) {
                    matched = true;
                    matchedBytes.add(base[bi]);

                    for (int i = 0; i < offset; i++) targetOnlyBytes.put(ti + i, target[ti + i]);
                    ti = ti + offset;
                    break;
                }
                if (target[ti] == base[bi + offset]) {
                    matched = true;
                    matchedBytes.add(target[ti]);

                    for (int i = 0; i < offset; i++) baseOnlyBytes.put(bi + i, base[bi + i]);
                    bi = bi + offset;
                    break;
                }
            }
            if (!matched) {
                baseOnlyBytes.put(bi, base[bi]);
                targetOnlyBytes.put(ti, target[ti]);
            }
            bi++;
            ti++;
        }
        for (; bi < base.length; bi++) baseOnlyBytes.put(bi, base[bi]);
        for (; ti < target.length; ti++) targetOnlyBytes.put(ti, target[ti]);
    }

    public void clearResult() {
        matchedBytes.clear();
        baseOnlyBytes.clear();
        targetOnlyBytes.clear();
    }

    // -------- 結果を返すメソッド。参照をそのまま返す(イミュータブルではない)

    public ArrayList<Byte> getMatchedByteList() {
        return matchedBytes;
    }

    /**
     * @return 比較元のバイト配列のみに存在する値(比較元のインデックスと値のマップ)
     */
    public Map<Integer, Byte> getBaseOnlyByteMap() {
        return baseOnlyBytes;
    }

    /**
     * @return 比較先のバイト配列のみに存在する値(比較先のインデックスと値のマップ)
     */
    public Map<Integer, Byte> getTargetOnlyByteMap() {
        return targetOnlyBytes;
    }

}

このクラスは、次のように使う(次はJUnit 4を用いたテストコードの一部)。

@Test
public void testDiff() {
    byte[] base   = { 0x00, 0x00, 0x00, 0x11,       0x33,       0x44 };
    byte[] target = { 0x00,             0x11, 0x22, 0x33, 0x33       };

    BytesDiff bytesDiff = new BytesDiff(base, target);
    bytesDiff.diff(4);

    assertEquals(3, bytesDiff.getMatchedByteList().size());
    assertEquals(3, bytesDiff.getBaseOnlyByteMap().size());
    assertEquals(2, bytesDiff.getTargetOnlyByteMap().size());

    assertEquals(new Byte((byte) 0x00), bytesDiff.getMatchedByteList().get(0));
    assertEquals(new Byte((byte) 0x11), bytesDiff.getMatchedByteList().get(1));
    assertEquals(new Byte((byte) 0x33), bytesDiff.getMatchedByteList().get(2));

    assertEquals(new Byte((byte) 0x00), bytesDiff.getBaseOnlyByteMap().get(1));
    assertEquals(new Byte((byte) 0x00), bytesDiff.getBaseOnlyByteMap().get(2));
    assertEquals(new Byte((byte) 0x44), bytesDiff.getBaseOnlyByteMap().get(5));

    assertEquals(new Byte((byte) 0x22), bytesDiff.getTargetOnlyByteMap().get(2));
    assertEquals(new Byte((byte) 0x33), bytesDiff.getTargetOnlyByteMap().get(4));
}

十分にテストしたわけではないが、動作はしそう。
通常は、どんな風に実装するのだろうか。知りたい。。