1

についていくつか質問がありjava.nio.Bufferます。基本的に、私の質問は、flip()読み取りと書き込みを切り替えるために常に呼び出しが必要かどうか、またはデータが読み取られる前にデータが完全に書き込まれるようにするために、たとえば書き込みと読み取りの場合など、低速のI/Oにのみ必要かどうかから始まります。 。私の特定の質問はmappedByteBufferです。ファイルが存在し、私が知っているサイズである場合は、position(int newPosition)呼び出しを使用してファイルの任意の部分に移動し、読み取りまたは書き込みを実行できます。つまり、基本的に、バッファをメモリのチャンクとして使用して、マークまたは制限の概念。これは本当ですか?

次の例を考えてみましょう。最初から整数1、次に2を含むファイルがある場合、位置0に別の整数3を配置し、巻き戻して3と2をバッファーから読み取ることができるようです。通常の非mmapバッファーのように、制限によって2番目のgetIntから停止するべきではありませんか?マップされたByteBufferの書き込みと読み取りを切り替えるためにflip()を呼び出す必要があるのはいつですか?ありがとう!

final int FILESIZE = 1024;

RandomAccessFile fileHandle;
    FileChannel fileChannel;
    File testFile = new File("c:/temp/testbbrw.dat");
    fileHandle = new RandomAccessFile(testFile, "rw");

    fileChannel = fileHandle.getChannel();
    MappedByteBuffer mbb = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, FILESIZE);

    int pos, data;

    mbb.position(0);
    mbb.putInt(3);

    mbb.position(0);
    data=mbb.getInt();  //I get 3
    data=mbb.getInt();  //I get 2, which was written to the file before this program runs

    mbb.force();
    fileHandle.close();
4

3 に答える 3

2

これはBuffer.flipが行うことです

347    public final Buffer flip() {
348        limit = position;
349        position = 0;
350        mark = -1;
351        return this;
352    }

バッファを準備しているので、バッファの次の読み取り操作は位置0で開始し、現在の制限で終了します。バッファの変更が完了し、別の場所に移動またはコピーする準備ができていることを意味します(つまり、バッファを読み取ることを意味します)

于 2013-02-08T07:09:29.033 に答える
0

私の質問は、flip() 呼び出しが読み取りと書き込みを切り替えるために常に必要かどうか、またはデータが完全に書き込まれる前にデータが完全に書き込まれることを保証するために、低速の I/O (たとえば、書き込み後に読み取りの場合) にのみ必要なのかどうかから始まります。読んだ。

  1. あらゆる記述のBufferは、それを読み込める、または書き込める状態から始まりますが、これは同じことです。
  2. flip()書き込みまたは取得できる状態にしますが、これは同じことです。
  3. その (非常にばかげた) 名前にもかかわらず、flip()は の逆ではありませんflip()。その唯一の逆数はcompact()clear()です。

わかりやすくするために、常に aBufferを読み取り可能な状態のままにし、必要なときにのみ書き込み可能な状態にし、その後すぐに読み取り可能な状態に戻すのが最善だと思います。

これは I/O 用です。

あなたがちょうどやっていてget()put()私がまったく使用するかどうかわからない場合flip()、これは確かにまたはMappedByteBufferを呼び出すことはありません。どちらもファイルにひどいことをする可能性があり、.clear()compact()flip()

于 2013-02-08T07:29:09.990 に答える
0

Javaでの API の設計はBuffer、典型的な循環型の有限バッファーに比べてわかりにくく、直感に反します。事態をさらに悪化させているのは、ドキュメント内の用語の選択の誤りです。これは、読み取り/書き込みおよびプット/取得という用語のあいまいな使用によって悪化し、前者は を使用した外部操作 (通常は a による) を指し、Channel後者Bufferによって提供される操作を指しBufferます。

Java バッファ

作成時には、新しいバッファは「空」であり、満たす準備ができています。コンストラクターで一部のコンテンツがすぐに提供される場合がありますが、「塗りつぶし」状態のままです。

このflip()メソッドは、バッファの論理状態を満杯から空に「反転」します。flip()通常の英語では、通常、論理的に可逆的なアクションを説明しますが、ばかげてそれ自体を逆にすることはありません。実際、コードを見ると、介入せずに 2 回呼び出しclearたりcompact、バッファを無効な状態に設定したりして、他のメソッドがナンセンスを返すようになっています。[1]

clear()メソッドとメソッドはのcompact()論理的な逆でありflip()、バッファを「いっぱいになった」状態に復元します。前者もバッファを空にし、後者は残りのコンテンツを維持します。

一般的な推奨事項は、try/finally を使用して、特定のバッファーを常に一貫した状態に保つことです。例えば:

ByteBuffer wrap(ByteBuffer src, ByteBuffer tgt) {
    // assume buffers are *always* kept in the "filling" state
    try {
        src.flip(); // change `src` to "emptying"; assume tgt already filling
        // transfer some or all of `src` to `tgt`
        }
    finally {
        if(src.remaining()) { src.compact(); } // revert `src` to "filling" without discarding remaining data
        else                { src.clear();   } // compact() is (usually) less efficient than clearing
        }

典型的な有限巡回バッファ

Java バッファーが最も直観に反するのは、ほとんどの循環バッファー実装が同時に読み取り/書き込み可能であり、3 つの値を維持するheadためtailですcapacityそして、それらは単に値が折り返されることを許可します。はデータのhead読み取り元であり、 は書き込み先tailです。下層の配列の最後に到達すると、head/tail の値は単純に 0 に設定されます (つまり、循環します)。

の場合head == tail、バッファは空です。の場合inc(tail) == head、バッファはいっぱいで、現在のコンテンツの長さは までに到達しhead <= tail ? (tail - head) : (capacity - head + tail)ます。バッキング配列のサイズは、通常capacity+1tailインデックスがバッファーがいっぱいの場合と等しくないようheadになっています (別のフラグがないとあいまいになります)。

これにより、内部インデックスの処理が少し複雑になりますが、状態をフリップフロップする必要がなく、データを内部配列の先頭に「圧縮」する必要がないというメリットがあります (ただし、ほとんどの実装では、開始/終了インデックスを次のようにリセットします)。バッファーが空になるたびにゼロ)。

通常、これは、2 つの配列コピーが必要になる可能性があることを読み取るときに、トレードオフにもなります。最初に からhead配列の末尾まで、次に配列の先頭から までtail。ターゲットがバッファでもあり、書き込み中にラップする場合は、3 つのコピー操作が必要になることがあります (ただし、余分なコピーはputメソッド内に隠されています)。

仮定

私の最善の推測は、Java がこの方法でバッファを定義し、バッファへのすべての読み取りと書き込みが連続したブロックで行われるようにすることです。これにより、おそらく、ソケット、メモリマップ、チャネルなどを処理する際にダウンストリーム/内部の最適化が可能になり、中間コピーを作成する必要がなくなります。

ここで推測するだけです。

ノート

[1] ダブル フリッピングにより制限が ではなく 0 に設定されるため、無効です。これは、 <= 0 または位置 >= 制限であるため、ほとんどの内部メソッドでcapacity発生します。例えば:BufferOverflowExceptionlimit - position

  511       final int nextPutIndex() {                          // package-private
  512           if (position >= limit)
  513               throw new BufferOverflowException();
  514           return position++;
  515       }
  516   
  517       final int nextPutIndex(int nb) {                    // package-private
  518           if (limit - position < nb)
  519               throw new BufferOverflowException();
  520           int p = position;
  521           position += nb;
  522           return p;
  523       }
  524   
  525       /**
  526        * Checks the given index against the limit, throwing an {@link
  527        * IndexOutOfBoundsException} if it is not smaller than the limit
  528        * or is smaller than zero.
  529        */
  530       final int checkIndex(int i) {                       // package-private
  531           if ((i < 0) || (i >= limit))
  532               throw new IndexOutOfBoundsException();
  533           return i;
  534       }
于 2017-05-10T20:25:39.447 に答える