9

byte[]一連のメッセージを読んでユーザーに返すためのGCフレンドリーなコードを書いています。内部的には同じものを再利用します。つまり、ほとんどの場合、同じインスタンスByteBufferを繰り返し返します。byte[]

注意のjavadocを作成し、これをユーザーに公開することを検討していますIterator<byte[]>。契約に違反することはありませんが、違反した場合Iterator、ユーザーは確かに驚かれる可能性がありLists.newArrayList(myIterator)ます。Listbyte[]

質問:同じオブジェクトを変更して返す可能性のあるクラスがインターフェイスを実装するのは悪い習慣ですか?Iterator

  • もしそうなら、最良の選択肢は何ですか?「オブジェクトを変更/再利用しないでください」は簡単な答えです。ただし、再利用が非常に望ましい場合については説明していません。

  • そうでない場合、驚き最小の原則に違反することをどのように正当化しますか?

2つのマイナーノート:

  • 私はGuavaを使用しているAbstractIteratorので、remove()は実際には問題ではありません。

  • 私のユースケースでは、ユーザーはであり、このクラスの表示は制限されますが、より広く適用できるように、これを一般的に十分に尋ねようとしました。

更新:キースの投票数の3倍であるため、ルイの回答を受け入れていますが、私のユースケースでは、キースの本番環境への回答についてコメントに残したコードを使用する予定であることに注意してください。

4

3 に答える 3

10

EnumMap本質的にこれをentrySet()イテレータで実行したため、今日まで混乱し、狂気の、気のめいるようなバグが発生します。

もし私があなたなら、私は使用しませんIterator-私は別のAPIを作成し(おそらくIteratorとはまったく異なります)、それを実装します。たとえば、メッセージを書き込むためのを入力として受け取る新しいAPIを作成ByteBufferて、APIのユーザーがバッファを再利用するかどうかを制御できるようにすることができます。ByteBufferこれは、不必要に雑然としたコードを作成することなく、かなり直感的に思えます(ユーザーは明らかにきれいに再利用するコードを書くことができます)。

于 2012-08-09T23:39:53.650 に答える
7

無効にできる中間オブジェクトを定義します。したがって、関数はを返し、Iterator<ByteArray>次のByteArrayようになります。

class ByteArray {
    private byte[] data;
    ByteArray(byte[] d) { data = d; }
    byte[] getData() {
        if (data == null) throw new BadUseOfIteratorException();
        return data;
    }
    void invalidate() { data = null; }
}

次に、イテレータは以前に返されたものを無効にして、( 、またはあなたが提供する他のアクセサをByteArray介した)将来のアクセスが失敗するようにすることができます。getDataそうすれば、少なくとも誰かがのようなことをすると、間違ったデータを黙って返すのではなく、Lists.newArrayList(myIterator)少なくともエラーが発生します(最初の無効なものにアクセスしたとき)。ByteArray

もちろん、これは考えられるすべての悪い使用法を捕らえるわけではありませんが、おそらく一般的な使用法です。byte[]生を返さず、代わりにのようにアクセサを提供することに満足している場合はbyte get(int idx)、すべてのケースをキャッチする必要があります。

イテレータの戻りごとに新しいものを割り当てる必要がありますが、イテレータByteArrayの戻りごとにコピーするよりもはるかに安価であることが望まれますbyte[]

于 2012-08-09T23:47:32.180 に答える
1

キース・ランドールと同じように、私も作成しますIterator<ByteArray>が、動作はまったく異なります(以下の注釈はロンボクからのものです)。

@RequiredArgsConstructor
public class ByteArray {
    @Getter private final byte[] data;
    private final ByteArrayIterable source;
    void allowReuse() {
        source.allowReuse();
    }
}

public class ByteArrayIterable implements Iterable<ByteArray> {
    private boolean allowReuse;
    public allowReuse() {
        allowReuse = true;
    }
    public Iterator<ByteArray> iterator() {
        return new AbstractIterator<ByteArray>() {
            private ByteArray nextElement;
            public ByteArray computeNext() {
                if (noMoreElements()) return endOfData();
                if (!allowReuse) nextElement =
                    new ByteArray(new byte[length], ByteArrayIterable.this);
                allowReuse = false;
                fillWithNewData(lastElement.getData());
            }
        }
    }
}

Lists.newArrayList(myIterator)これで、いつものように呼び出しで新しいバイト配列が割り当てられるため、すべてが機能します。のようなあなたのループで

for (ByteArray a : myByteArrayIterable) {
    a.allowReuse();
    process(a.getData());
}

バッファは再利用されます。allowReuse()誤って電話をかけない限り、害はありません。呼び出すのを忘れると、パフォーマンスは低下しますが、動作は正しくなります。


今、私はそれがなくても動作する可能性があることを確認しましByteArrayた。重要なことは、myByteArrayIterable.allowReuse()呼び出されることです。これは直接実行できます。

于 2012-08-10T03:12:29.980 に答える