zoukankan      html  css  js  c++  java
  • 缓存

    缓存的意思是中间存储,相当于中转站,积累一定的货物,再往目的地运送。如果没有中转站,就会出现一件一件的运送,耗费大量的人力物力。

    缓存的基类是:Buffer

    缓存的基本子类有:Char/Byte/Short/Int/Long/Float/Double + Buffer

    缓存一般用一个数组做存储,array()方法可以获取这个数组,但并不是所有Buffer都能够获取这个数组,使用hasArray()方法的返回值,查看Buffer是否能获取数组。有两种情况,Buffer是不能获取数组。

    第一种情况,直接存储的Buffer:使用new创建一个数组,这个数组使用的存储空间是堆空间,如果使用JIN,然后直接从内存中开辟空间,这样就可以节省堆空间,解决一些堆空间不足的情况。因此,Buffer分堆存储,和直接存储。堆存储的Buffer使用allocate(int)静态方法创建,直接存储的Buffer使用allocateDirect(int)静态方法创建,wrap(...)静态方法是自己提供一个数组来创建Buffer,自己提供的数组肯定是属于堆空间上的。使用isDirect()方法,可以查看Buffer是否为直接储存,否则为堆存储。

    第二种情况,只读模式的Buffer:缓存的基本操作就是读和写,使用get()方法进行读取操作,使用put()方法进行写入操作。Buffer创建的时候,既可以读,也可以写。有一种需求,Buffer需要提供给其它操作者,但只能读,不能写。使用asReadOnlyBuffer()方法就能创建出一个只读Buffer,这个只读Buffer与原Buffer是共享同一个数组,共享数组可以达到节省空间的目的。因此,为了不让操作者进行写操作,只读模式的Buffer就不允许操作者获得里面的数组。使用isReadOnly()方法,查看Buffer是否只读。

    缓存在读取和写入数组单元时,其操作位与实际操作位会有偏差,使用arrayOffset()方法,可以获得这个偏差量。以下方法的参数和返回值,都是相对于偏差量的。

    • 当前操作位:position()/position(int)
    • 操作结束位:limit()/limit(int)
    • 操作结束位的最大值:capacity()

    每次操作完后,当前操作位就会指向下一个数组单元。一直到操作结束位时,缓存就不允许再操作。通过limit()-position(),也可以使用remaining()方法,查看剩余操作量。使用hasRemaining()方法,查看是否还能操作。

    当前操作位与操作结束位可以通过position(int)和limit(int)方法单独调整,也可以通过其它方法进行整体调整。

    • clear()方法,调整position为0,limit为capacity,表示清空数据,等待新数据写入。
    • flip()方法,调整limi为position,position为0,表示从写模式切换为读模式。
    • rewind()方法,调整position为0,表示重头操作(读/写)。
    • reset()方法,调整position为回档位,表示操作从回档位开始。回档位是需要事先使用mark()方法标记当前操作位为回档位,回档位不能大于当接操作位,否则在调整过程中,回档位被消除。
    • compact()方法,把未读数据复制至开始位置,调整position为剩余量,调整limit为capacity,表示清除已读数据,保留未读数据,并等待写入数据。

    (注:图源于http://www.cnblogs.com/hvicen/p/6138690.html)

    该图表示出Buffer在写与读的时候,limit所处的位置,左边是写模式,右边是读模式。使用flip()方法,使写模式转变为读模式。使用clear()和compact()方法,使读模式转变为写模式,clear()是清空所有数据,compact()只清除已读数据,保留未读数据。

    使用duplicate()方法,拷贝出一个属性和状态一致的Buffer,两个Buffer是共享数组。

    使用slice()方法,构建出一个Buffer,这个Buffer以当前操作位的实际操作位为偏差量,以剩余操作量为操作结束位的最大值。换句话说,slice的意思是切片,切出一个从当前操作位到操作结束位的Buffer,这个Buffer的读与写只能在这一段上面进行,同样切出来的Buffer与原Buffer是共享数组。

    一个数据单元可以切分成多个字节,例如一个整型数据可以切分成四个字节,存储在数组时,可以从高位到低位存储,也可以从低位到高位存储。使用order()/order(ByteOrder)方法,可以查看和设置一个数据单元的排列方式,低位到高位排列为ByteOrder.BIG_ENDIAN,高位到低位排列为ByteOrder.LITTLE_ENDIAN。使用ByteOrder.nativeOrder()方法,查看本地的数据排列方式。

    关于CharBuffer与ByteBuffer之间的编码与解码的转换,如图所示:

    示例代码:

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.charset.Charset;
    import java.nio.charset.CharsetDecoder;
    import java.nio.charset.CharsetEncoder;
    import java.nio.charset.CoderResult;
    
    public class Test {
        public static void main(String[] args) throws IOException, InterruptedException {
            String fileName = "f:/test.txt";
            writeFile(fileName);
            readFile(fileName);
        }
        
        public static void writeFile(String fileName) throws IOException{
            CharBuffer cb = CharBuffer.allocate(100);
            cb.put("中文abcdefg");
            cb.flip();
            
            // FileOutputStream file = new FileOutputStream(fileName);
            RandomAccessFile file = new RandomAccessFile(fileName, "rw");
            int length = 0;
            FileChannel channel = file.getChannel();
    
            // 创建字节缓存,为测试效果,设置空间大小为5
            ByteBuffer bb = ByteBuffer.allocate(5);
            // 创建编码器
            CharsetEncoder encoder = Charset.defaultCharset().newEncoder();
            // 设置字符编码不完整时的处理方式
            // encoder.onMalformedInput(CodingErrorAction.REPLACE);
            // 设置字符编码不能映射时的处理方式
            // encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
    
            CoderResult result = null;
            boolean end = false;
            while(true){
                if(!end) {
                    // 编码,第3个参数表示告诉编码器,输入空间的数据是否已经是最后一批数据
                    result = encoder.encode(cb, bb, true);
                } else {
                    // 添加尾部:有些字符集需要添加尾部
                    result = encoder.flush(bb);
                }
                // 下溢,表示输入数据不足,即数据转换完毕;
                if(result.isUnderflow()){
                    if(!end)
                        end = true;
                    else {
                        // 切换读模式
                        bb.flip();
                        // 写入文件
                        length += channel.write(bb);
                        // 测试是否还有数据未写入文件
                        if(bb.hasRemaining()){
                            throw new RuntimeException("还有剩余数据未写入");
                        }
                        break;
                    }
                }
                // 上溢,表示输出空间不足;
                else if(!result.isOverflow())
                    result.throwException();
                // 切换读模式
                bb.flip();
                // 写入文件
                length += channel.write(bb);
                // 切换写模式
                bb.compact();
            }
            
            // 设置文件大小
            file.setLength(length);
            // file.flush();
            // 关闭通道
            channel.close();
            // 关闭文件
            file.close();
        }
        
        public static void readFile(String fileName) throws IOException{
            CharBuffer cb = CharBuffer.allocate(100);
            
            FileInputStream file = new FileInputStream(fileName);
            // RandomAccessFile file = new RandomAccessFile(fileName, "rw");
            FileChannel channel = file.getChannel();
            
            // 创建字节缓存,为测试效果,设置空间大小为5
            ByteBuffer bb = ByteBuffer.allocate(5);
            bb.flip();
            
            // 创建解码器
            CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
            // 设置字符编码不完整时的处理方式
            // decoder.onMalformedInput(CodingErrorAction.REPLACE);
            // 设置字符编码不能映射时的处理方式
            // decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
            
            CoderResult result = null;
            boolean eof = false;
            do {
                // 切换写模式
                bb.compact();
                // 读取文件
                eof = (channel.read(bb) == -1);
                // 切换读模式
                bb.flip();
                // 解码,第3个参数表示告诉解码器,输入空间的数据是否已经是最后一批数据
                result = decoder.decode(bb, cb, eof);
                // 下溢,表示输入数据不足;上溢,表示输出空间不足;
                if(!result.isUnderflow())
                    result.throwException();
            } while(!eof);
            // 添加尾部:有些字符集需要添加尾部
            result = decoder.flush(cb);
            // 下溢,表示输入数据不足;上溢,表示输出空间不足;
            if(!result.isUnderflow())
                result.throwException();
            
            // 关闭通道
            channel.close();
            // 关闭文件
            file.close();
    
            // 切换读模式
            cb.flip();
            // 打印读取的文件内容
            System.out.println(cb.toString());
        }
    }

     (参考文献:《Java字符编码解码的实现详解》

    本文原创,待续更新!

  • 相关阅读:
    android界面横屏和竖屏的切换
    google 提供webrtc 的实例使用 turnserver的方式
    如何使官方提供的AppRTCDemo 运行在自己搭建的server(官方提供的apprtc)上(官方的server源码)
    android在全屏下第一次触摸屏幕没有触发事件
    ubuntu常用命令记录集
    python 一个包中的文件调用另外一个包文件 实例
    python-插入排序
    phantomjs submit click
    python socket.error: [Errno 10054] 解决方法
    python-快速排序,两种方法→易理解
  • 原文地址:https://www.cnblogs.com/hvicen/p/6146154.html
Copyright © 2011-2022 走看看