Java NIO —— Buffer(缓冲区)

Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区。注意:Buffer是非线程安全类。

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。

NIO 有以下几种Buffer类型:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

capacity

作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。

capacity一旦初始化 
后就不会改变,其值一直为常量。在使用中我们一般使用Buffer的抽象子类ByteBuffer.allocate()方法,实际上是生成ByteArrayBuffer类。

position

当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1。

当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0。当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。

limit

在读模式下,Buffer的limit表示你最多能从Buffer里读多少数据。 写模式下,limit等于Buffer的capacity。

当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)

(1)Buffer中定义的变量含义

/**
 * <code>UNSET_MARK</code> means the mark has not been set.
 */
static final int UNSET_MARK = -1;

/**
 * The capacity of this buffer, which never changes.
 */
final int capacity;

/**
 * <code>limit - 1</code> is the last element that can be read or written.
 * Limit must be no less than zero and no greater than <code>capacity</code>.
 */
int limit;

/**
 * Mark is where position will be set when <code>reset()</code> is called.
 * Mark is not set by default. Mark is always no less than zero and no
 * greater than <code>position</code>.
 */
int mark = UNSET_MARK;

/**
 * The current position of this buffer. Position is always no less than zero
 * and no greater than <code>limit</code>.
 */
int position = 0;

/**
 * The log base 2 of the element size of this buffer.  Each typed subclass
 * (ByteBuffer, CharBuffer, etc.) is responsible for initializing this
 * value.  The value is used by JNI code in frameworks/base/ to avoid the
 * need for costly ‘instanceof‘ tests.
 */
final int _elementSizeShift;

/**
 * For direct buffers, the effective address of the data; zero otherwise.
 * This is set in the constructor.
 */
final long effectiveDirectAddress;

(2)clear()方法用于写模式,其作用为清空Buffer中的内容,所谓清空是指写上限与Buffer的真实容量相同,即limit==capacity,同时将当前写位置置为最前端下标为0处。代码如下:

/**
 * <code>UNSET_MARK</code> means the mark has not been set.
 */
static final int UNSET_MARK = -1;

/**
 * Clears this buffer.
 * <p>
 * While the content of this buffer is not changed, the following internal
 * changes take place: the current position is reset back to the start of
 * the buffer, the value of the buffer limit is made equal to the capacity
 * and mark is cleared.
 *
 * @return this buffer.
 */
public final Buffer clear() {
    position = 0; //设置当前下标为0
    mark = UNSET_MARK; //取消标记
    limit = capacity; //设置写越界位置与和Buffer容量相同
    return this;
}

(3)reset()方法和clear()方法一样用于写模式,区别是reset()的作用是丢弃mark位置以后的数据,重新从mark位置开始写入,且mark不能未设置;而clear是从0位置开始重新写入。

  • /**
     * Sets this buffer‘s mark at its position.
     *
     * @return  This buffer
     */
    public final Buffer mark() {
        mark = position;
        return this;
    }
    
    /**
     * Resets the position of this buffer to the <code>mark</code>.
     *
     * @return this buffer.
     * @throws InvalidMarkException
     *                if the mark is not set.
     */
    public final Buffer reset() {
        if (mark == UNSET_MARK) {
            throw new InvalidMarkException("Mark not set");
        }
        position = mark;
        return this;
    }

(4)rewind()在读写模式下都可用,它单纯的将当前位置置0,同时取消mark标记,仅此而已;也就是说写模式下limit仍保持与Buffer容量相同,只是重头写而已;读模式下limit仍然与rewind()调用之前相同,也就是为flip()调用之前写模式下的position的最后位置,flip()调用后此位置变为了读模式的limit位置,即越界位置,代码如下:

/**
 * Rewinds this buffer.
 * <p>
 * The position is set to zero, and the mark is cleared. The content of this
 * buffer is not changed.
 *
 * @return this buffer.
 */
public final Buffer rewind() {
    position = 0;
    mark = UNSET_MARK;
    return this;
}

(5)flip()函数的作用是将写模式转变为读模式,即将写模式下的Buffer中内容的最后位置变为读模式下的limit位置,作为读越界位置,同时将当前读位置置为0,表示转换后重头开始读,同时再消除写模式下的mark标记,代码如下

/**
 * Flips this buffer.
 * <p>
 * The limit is set to the current position, then the position is set to
 * zero, and the mark is cleared.
 * <p>
 * The content of this buffer is not changed.
 *
 * @return this buffer.
 */
public final Buffer flip() {
    limit = position;
    position = 0;
    mark = UNSET_MARK;
    return this;
}

(6)remaining()仅在读模式下使用,用来获取还未读出的字节数。

  • /**
     * Returns the number of remaining elements in this buffer, that is
     * {@code limit - position}.
     *
     * @return the number of remaining elements in this buffer.
     */
    public final int remaining() {
        return limit - position;
    }

(7)Buffer的抽象子类ByteBuffer的compact()方法也蛮重要的。compact()的作用是压缩数据。比如当前EOF是6,当前指针指向2(即0,1的数据已经写出了,没用了),那么compact方法将把2,3,4,5的数据挪到0,1,2,3的位置,然后指针指向4的位置。这样的意思是,从4的位置接着再写入数据。

/**
 * Compacts this byte buffer.
 * <p>
 * The remaining bytes will be moved to the head of the
 * buffer, starting from position zero. Then the position is set to
 * {@code remaining()}; the limit is set to capacity; the mark is
 * cleared.
 *
 * @return {@code this}
 * @throws ReadOnlyBufferException
 *                if no changes may be made to the contents of this buffer.
 */
public abstract ByteBuffer compact();

(8)equals()

当满足下列条件时,表示两个Buffer相等:

  • 有相同的类型(byte、char、int等)。
  • Buffer中剩余的byte、char等的个数相等。
  • Buffer中所有剩余的byte、char等都相同。

equals只是比较Buffer的一部分,不是每一个在它里面的元素都比较(即它只比较Buffer中的剩余元素)。

以ByteBuffer为例

  • /**
     * Tells whether or not this buffer is equal to another object.
     *
     * <p> Two byte buffers are equal if, and only if,
     *
     * <ol>
     *
     *   <li><p> They have the same element type,  </p></li>
     *
     *   <li><p> They have the same number of remaining elements, and
     *   </p></li>
     *
     *   <li><p> The two sequences of remaining elements, considered
     *   independently of their starting positions, are pointwise equal.
     *   </p></li>
     *
     * </ol>
     *
     * <p> A byte buffer is not equal to any other type of object.  </p>
     *
     * @param  ob  The object to which this buffer is to be compared
     *
     * @return  <tt>true</tt> if, and only if, this buffer is equal to the
     *           given object
     */
    public boolean equals(Object ob) {
        if (this == ob)
            return true;
        if (!(ob instanceof ByteBuffer))
            return false;
        ByteBuffer that = (ByteBuffer)ob;
        if (this.remaining() != that.remaining())
            return false;
        int p = this.position();
        for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
            if (!equals(this.get(i), that.get(j)))
                return false;
        return true;
    }

(9)compareTo()

compareTo()方法比较两个Buffer的剩余元素(byte、char等), 如果满足下列条件,则认为一个Buffer“小于”另一个Buffer:

  • 第一个不相等的元素小于另一个Buffer中对应的元素 。
  • 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。

以ByteBuffer为例

  • /**
     * Compares this buffer to another.
     *
     * <p> Two byte buffers are compared by comparing their sequences of
     * remaining elements lexicographically, without regard to the starting
     * position of each sequence within its corresponding buffer.
     * Pairs of {@code byte} elements are compared as if by invoking
     * {@link Byte#compare(byte,byte)}.
    
     *
     * <p> A byte buffer is not comparable to any other type of object.
     *
     * @return  A negative integer, zero, or a positive integer as this buffer
     *          is less than, equal to, or greater than the given buffer
     */
    public int compareTo(ByteBuffer that) {
        int n = this.position() + Math.min(this.remaining(), that.remaining());
        for (int i = this.position(), j = that.position(); i < n; i++, j++) {
            int cmp = compare(this.get(i), that.get(j));
            if (cmp != 0)
                return cmp;
        }
        return this.remaining() - that.remaining();
    }

补充:

  • 由于ByteBuffer是非线程安全的,所以多线程访问的时候也必须加锁。
  • ByteBuffer在内部也是利用byte[]作为内存缓冲区,只不过多提供了一些标记变量而已。当多线程访问的时候,可以清楚的知道当前数据的位置。

转载出处——http://blog.csdn.net/chy555chy/article

时间: 2024-10-13 20:59:48

Java NIO —— Buffer(缓冲区)的相关文章

Java NIO之缓冲区

简介 IO概念 缓冲区操作 虚拟内存 文件IO 流IO 缓冲区 Buffer属性 Buffer数据填充.翻转.释放.压缩.标记 Buffer比较 Buffer批量移动 复制缓冲区 字节缓冲区 直接缓冲区 其他缓冲区 简介 几个IO事实: 影响应用程序执行效率的限定因素,往往非处理速率,而是IO OS要移动大块数据,往往是在DMA协助下完成,而JVM的IO操作往往是小块数据,有了NIO,可改变这种情况 JDK1.4,java.nio提供了一套新的抽象用于IO处理 IO概念 缓冲区操作 进程执行IO

JAVA NIO buffer (知识三)

java nio 里的buffer是缓存数据,通常缓冲区是一个数组,字节数组,也可以是别的类型.最常用的就是bytebuffer, 还有一些其它的类型: charbuffer, shortbuffer, intbuffer, longbuffer, floatbuffer, doublebufer. 一开始在知识(一)里写到,想要用nio读取数据,都是从channel读取到buffer.然后应用从buffer读取数据,同样写数据也是,先把数据写到buffer中,然后读道channel中. 基本上

Java NIO Buffer

Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.交互图如下: 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存. 下面是NIO Buffer相关的话题列表: 1.Buffer的基本用法 使用Buffer读写数据一般遵循以下四个步骤: 写入数据到Buffer 调用flip()方法 从Buffer中读取数据 调用clear()方法或者com

Java NIO 之缓冲区

一个 Buffer 对象是固定数量的数据的容器.通道是 I/O 传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标. 缓冲区基础 所有的缓冲区都具有四个属性来 供关于其所包含的数据元素的信息. capacity(容量):缓冲区能够容纳数据的最大值,创建缓冲区后不能改变. limit(上界):缓冲区的第一个不能被读或写的元素.或者,缓冲区现存元素的计数. position(位置):下一个要被读或写的元素的索引.调用 get 或 put 函数会更新. mark(标记):一个备忘位置.调用 ma

【JAVA】【NIO】4、Java NIO Buffer

Java NIO的Buffer用于和channel进行交互. buffer本质上是一个内存块,你可以写数据,然后读取出来. 这个内存块是通过NIO的Buffer对象进行包装的,该对象提供了一系列的方法,使得对内存块的访问更加容易了. 基本的Buffer使用 使用Buffer读写数据一般有如下4步: 1.将数据写入 Buffer 2.调用buffer.flip()方法 3.从Buffer中读出数据 4.调用buffer.clear()方法或buffer.compact()方法 当你将数据写入buf

zbb20180930 java,nio直接缓冲区与非直接缓冲区别

直接缓冲区与非直接缓冲区别 分散读取与聚集写入 非直接缓冲区 通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中! 案例展示 这里提供一个名为Python1,大小为397.1MB的zip文件: 现分别用直接缓冲区和非直接缓冲区对上面文件进行文件复制,看哪个缓冲区耗时最短,效率更高? 代码示例 public class NoStraightChannel { public static void main(String[] args) throws IOException

Java NIO -- 直接缓冲区与非直接缓冲区

直接缓冲区与非直接缓冲区: 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中.可以提高效率 字节缓冲区要么是直接的,要么是非直接的.如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在机 此缓冲区上执行本机 I/O 操作.也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后), 虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中

java NIO buffer --directBuffer (2)

HeapBuffer ----堆缓冲    :其实是在java 的内存模型中,java 虚拟机可以直接管控的 DirectBuffer ---直接缓冲 :使用的是native ,与操作系统挂钩,调用的是c 或者c++ 的代码,不在java 的内存模型中,我们称为堆外内存,因为不属于java 内存模型,所以java 虚拟机管控不到: 但是 address 维护了堆外内存的引用 (在buffer 类中维护者 long address) 那么问题来了,为什么不直接用HeapBuffer 操作,反而 使

Java NIO Buffer详解

一.ByteBuffer类型化的put与get方法 /** * ByteBuffer类型化的put与get方法 */ public class NioTest5 { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(64); buffer.putInt(5); buffer.putLong(500000000L); buffer.putDouble(13.456); buffer.pu