现在的位置: 首页 > 综合 > 正文

Java 缓冲区

2014年01月13日 ⁄ 综合 ⁄ 共 4698字 ⁄ 字号 评论关闭

1 缓冲区基础

     缓冲区是包在一个对象内的基本数据元素数组。相比简单数组,它将数据内容和信息包含在一个单一的对象中。

     缓冲区类图:


1.1 属性

      容量(Capacity):缓冲区能够容纳的数据元素的最大数量,被设定后,永远不能被改变。
      上界(Limit):缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
      位置(Position):下一个被读或被写的元素的索引,位置会自动由相应的get()和set()方法更新。

      标记(Mark):备忘位置。调用mark()来设定mark=position,调用reset()设定postion=mark。标记在设定前是未定义的。

1.2 存取

     缓冲区管理固定数目的数据元素。但在任何特定的时刻,在我们想清空缓冲区之前,我们可能只是用了缓冲区的一部分。这时,我们需要能够追
踪添加到缓冲区的数据元素的数量,放入下一个元素的位置等的方法。位置属性可以做到这点,调用put()方法时指出了下一个数据元素应该被插入的
位置,或者get()被调用时指出下一个元素应从何处检索。
     

public abstract class ByteBuffer  
        extends Buffer implements Comparable  
{  
        // This is a partial API listing  
  
        public abstract byte get(  );  
        public abstract byte get (int index);  
        public abstract ByteBuffer put (byte b);  
        public abstract ByteBuffer put (int index, byte b);  
}

     对于put()方法,如果运算会导致位置超出上界,就会抛出BufferOverflowException异常,对于get(),如果位置不小于上界,就会抛出BufferUnderflowException异常。结对存取不会影响缓冲区的位置属性,但如果提供索引超出范围,也抛出IndexOutOfBoundsException。

1.3  填充

    buffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l').put((byte)'o');
    在Java中,字符在内部以Unicode码表示,每个Unicode字符占16位,所以上例中每个字符都必须被强制转换为byte。在不丢失位置的情况下进行
一些更改该怎么办呢?put()的绝对方案可以达到这样的目的。
    buffer.put(0,(byte)'M').put((byte)'w');
    绝对put方法不会影响position

1.4 翻转

    想把这个缓冲区传递给一个通道,以使内容能被全部写出,如果通道现在在缓冲区上执行get(),那么它将从我们刚插入的的有用数据之外取出未定义数据。如果我们将位置重新设为0,通道就会从正确位置开始获取。上界属性指明了缓冲区有效内容的末端。
    我们需要将上界属性设置为当前位置,然后将位置设置为0:buffer.limit(buffer.position()).position(0); 
    Buffer.flip()方法实现了上述功能:将一个能继续添加数据元素的填充状态的缓冲翻转成一个准备读出元素的释放状态。   
 
    Rewind()方法与 flip()相似,但不影响上界属性。它只是将位置值设回 0。您可以使用 rewind()后退,重读已经被翻转的缓冲区中的数据。 

1.5 释放

    如果接受到一个在别处被填满的缓冲区,肯能需要在检索内容之前将其翻转,如果一个通道的read()操作完成,而你想查看被通道放入缓冲区内
的数据,那么需要在调用get()之前翻转缓冲区。通道对象在缓冲区上调用put()增加数据;put和read可以随意混合使用。
    布尔方法hasRemaining()会在释放缓冲区时告知是否达到上界。
    缓冲区并不是线程安全的,如果多线程同时存取特定的缓冲区,需要在缓冲区之前进行同步。          

    clear()方法将缓冲区重置为空状态,它不改变缓冲区中的任何数据元素,而是仅仅将上界设为容量的值,并把位置设为0。   

1.6 压缩

    有时想从缓冲区中释放一部分数据,而不是全部,然后重新填充。为了实现这点,未读的数据元素需要下移使得第一个元素索引为0。compact()方法实现了这点。

     调用compact()方法的作用就是丢弃已经释放的数据,保留未释放的数据,并使缓冲区对重新填充做好准备。

     

   压缩之后:

   

1.7 标记

   标记,使缓冲区记住一个位置并在之后将其返回。缓冲区的标记在mark()被调用之前是未定义的,调用时标记被设置为当前的位置(position)。 reset()将位置设为当前的标记值。
   如果标记值未定义,调用reset()导致InvalidMarkException异常。rewind(),clear(),flip()总会抛弃标记。

 buffer.position(2).mark().position(4); 

          

    如果这个缓冲区现在被传递给一个通道,两个字节(“ow”)将会被发送,而位置会前进到 6。如果我们此时调用 reset(  ),位置将会被设为标记,再次将缓冲区传递给通道将导致四个字节(“llow”)被发送。 

1.8 比较

public abstract class ByteBuffer  
        extends Buffer implements Comparable 
{  
        // This is a partial API listing    
        public boolean equals (Object ob)  
        public int compareTo (Object ob)  
} 

1.9 批量移动

public abstract class CharBuffer  
        extends Buffer implements CharSequence, Comparable  
{  
        // This is a partial API listing  
  
        public CharBuffer get (char [] dst)  
        public CharBuffer get (char [] dst, int offset, int length)  
  
        public final CharBuffer put (char[] src)  
        public CharBuffer put (char [] src, int offset, int length)  
        public CharBuffer put (CharBuffer src)  
  
        public final CharBuffer put (String src)  
        public CharBuffer put (String src, int start, int end)  

    批量移动总是具有指定的长度,如果您所要求的数量的数据不能被传送,那么不会有数据被传递,缓冲区的状态保持不变,同时抛出 BufferUnderflowException 异常。如果缓冲区中的数据不够完全填满数组,您会得到一个异常。

2 创建缓冲区

        在Java中主要有七种主要缓冲区类,每一种都具有一种Java语言中的非布尔类型的原始类型数据。这些类没有一种能够直接实例化,它们都是抽象类,但是包含静态工厂方法用来创建相应类的新的实例。

         

public abstract class CharBuffer  
        extends Buffer implements CharSequence, Comparable  
{  
        // This is a partial API listing  
  
        public static CharBuffer allocate (int capacity)  
  
        public static CharBuffer wrap (char [] array)  
        public static CharBuffer wrap (char [] array, int offset,   
  int length)  
  
        public final boolean hasArray(  )  
        public final char [] array(  )  
        public final int arrayOffset(  )  
}      

        新的缓冲区是由分配或者包装操作创建的。分配一个缓冲区对象并分配一个私有的空间来存储容量大小的数据元素。        

CharBuffer charbuffer = CharBuffer.wrap (myArray, 12, 42);

        创建了一个 position 值为 12,limit 值为 54,容量为 myArray.length 的缓冲区,这个缓冲区可以存取这个数组的全部范围,offset 和 length 参数只是设置了初始的状态。
Slice()方法可以提供一个只占用备份数组一部分的缓冲区.
通过 allocate()或者 wrap()方法创建的缓冲区通常都是间接的,间接的缓冲区使用备份数组。Boolean 型方法 hasArray()告诉您这个缓冲区是否有一个可存取的备份数组。

public abstract class CharBuffer  
        extends Buffer implements CharSequence, Comparable 
{  
        // This is a partial API listing            
        public static CharBuffer wrap (CharSequence csq)  
        public static CharBuffer wrap (CharSequence csq, int start,   
  int end)  
} 

3 复制缓冲区    

public abstract class CharBuffer  
        extends Buffer implements CharSequence, Comparable  
{  
        // This is a partial API listing  
  
        public abstract CharBuffer duplicate(  );  
        public abstract CharBuffer asReadOnlyBuffer(  );  
        public abstract CharBuffer slice(  );  
}

    当一个管理其他缓冲器所包含的数据元素的缓冲器被创建时,这个缓冲器被称为视图缓冲器。视图存储器总是通过调用已存在的存储器实例中的方法来创建。

CharBuffer buffer = CharBuffer.allocate (8);  
buffer.position (3).limit (6).mark(  ).position (5);  
CharBuffer dupeBuffer = buffer.duplicate(  );  
buffer.clear(  ); 

    Duplicate()方法创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上。

    asReadOnlyBuffer()方法来生成一个只读的缓冲区视图。这与duplicate()相同,除了这个新的缓冲区不允许使用 put(),并且其 isReadOnly()方法将会返回 true

CharBuffer buffer = CharBuffer.allocate (8); 
buffer.position (3).limit (5);  
CharBuffer sliceBuffer = buffer.slice(  ); 

    slice()创建一个从原始缓冲区的当前位置开始的新缓冲区,并且其容量是原始缓冲区的剩余元素数量(limit-position),这个新缓冲区与原始缓冲区共享一段数据元素子序列。分割出来的缓冲区也会继承只读和直接属性。
    

 

【上篇】
【下篇】

抱歉!评论已关闭.