bytebuffer.putint_get的用法和例句

bytebuffer.putint_get的用法和例句最近再看java的NIO,里面提到了几个基本的类,其中ByteBuffer是最基础的,用于Channel的读写传输数据使用。下面总结一下我理解的ByteBuffer。先从代码开始分析staticpublicvoidasIntBuffer(){ByteBufferbBuf=ByteBuffer.allocate(512);bBuf.putI

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

最近再看java的NIO,里面提到了几个基本的类,其中ByteBuffer是最基础的,用于Channel的读写传输数据使用。下面总结一下我理解的ByteBuffer。
先从代码开始分析

	static public void asIntBuffer() {
		ByteBuffer bBuf = ByteBuffer.allocate(512);
		bBuf.putInt(1);
		bBuf.putInt(2);
		bBuf.putInt(3);
		bBuf.putInt(4);
		bBuf.putInt(5);
		bBuf.putInt(6);
		bBuf.putInt(7);
		bBuf.flip();
		bBuf.putInt(8);
		bBuf.putInt(9);
		System.out.println("缓冲区Pos:" + bBuf.position() + "  缓冲区Limit:"
				+ bBuf.limit());
		System.out.println(bBuf.getInt());
		System.out.println(bBuf.getInt());
		System.out.println(bBuf.getInt());
		System.out.println(bBuf.getInt());
		System.out.println(bBuf.getInt());
	}

输出:

缓冲区Pos:8  缓冲区Limit:28
3
4
5
6
7

从上面的输出发现当flip()被调用之后如果在网buffer里面put数据会覆盖之前写入的数据,导致Position位置后移,如果在加一句get()就会出现java.nio.BufferUnderflowException异常,见下面的输出。

缓冲区Pos:8  缓冲区Limit:28
3
4
5
6
7
Exception in thread "main" java.nio.BufferUnderflowException
	at java.nio.Buffer.nextGetIndex(Buffer.java:498)
	at java.nio.HeapByteBuffer.getInt(HeapByteBuffer.java:355)
	at com.Demo.asIntBuffer(Demo.java:52)
	at com.Demo.main(Demo.java:22)

简单的分析一下put、get和flip的源代码。

ByteBuffer bBuf = ByteBuffer.allocate(512);

首先看allocate函数,通过传入一个capacity用来指定buffer的容量,返回了一个HeapByteBuffer的对象,该对象是ByteBuffer的一个子类,其构造函数直接调用了ByteBuffer的构造函数。

    public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }
HeapByteBuffer的构造函数:
    HeapByteBuffer(int cap, int lim) {            // package-private
        super(-1, 0, lim, cap, new byte[cap], 0);//初始化 limit pos cap mark等参数
        /*
        hb = new byte[cap];
        offset = 0;
        */
    }

    HeapByteBuffer(byte[] buf, int off, int len) { // package-private
        super(-1, off, off + len, buf.length, buf, 0);
        /*
        hb = buf;
        offset = 0;
        */
    }

    protected HeapByteBuffer(byte[] buf,
                                   int mark, int pos, int lim, int cap,
                                   int off)
    {
        super(mark, pos, lim, cap, buf, off);
        /*
        hb = buf;
        offset = off;
        */
    }

此时我们就得到了一个带有Capacity大小缓冲区的ByteBuffer对象,下面开始往缓冲区写数据,以int类型数据为列子。来分析一下putInt(int i)的源码。putInt()的实现是在HeapByteBuffer类中,通过调用了Bits的静态函数putInt完成的,其中put之后pos的移动是通过nextPutIndex()函数完成,Int大小4个字节,向后移动4个,该函数实在Buffer基类中实现的。bigEndian是一个bool变量,用来表示当前是大端存储还是小端存储,默认大端。

   public ByteBuffer putInt(int x) {
        Bits.putInt(this, ix(nextPutIndex(4)), x, bigEndian);
        return this;
    }
    protected int ix(int i) {
        return i + offset;//加上位置偏移
    }
    final int nextPutIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;//Pos指针后移
        return p;//原始Pos指针返回,用来计算此次取出的数据
    }

下面看Bits的put函数:

    static void putInt(ByteBuffer bb, int bi, int x, boolean bigEndian) {
        if (bigEndian)//根据不同的存储方式调用不同的解析函数
            putIntB(bb, bi, x);
        else
            putIntL(bb, bi, x);
    }
    //以大端为例,这里主要是后面的intX()函数,用来对x进行位运算,取出相应位置的数据,放入到缓冲区的相应位置
    static void putIntB(ByteBuffer bb, int bi, int x) {
        bb._put(bi    , int3(x));
        bb._put(bi + 1, int2(x));
        bb._put(bi + 2, int1(x));
        bb._put(bi + 3, int0(x));
    }

    private static byte int3(int x) { return (byte)(x >> 24); }
    private static byte int2(int x) { return (byte)(x >> 16); }
    private static byte int1(int x) { return (byte)(x >>  8); }
    private static byte int0(int x) { return (byte)(x      ); }

到此位置,数据被放入到了缓冲区中,下面开始读取。读取之前一定要先调用flip()函数,该函数可以控制pos和limit的值,使得缓冲区可以在读写之间很好的切换,它的实现实在Buffer基类中,主要工作就是,limit转换成当前缓冲区在最后一次写入数据后的位置,pos和mark重置,从头开始读取数据,这就是为什么,在写入之后调用flip()函数在写入不但会覆盖之前写入的值,还会导致pos位置发生变化,不能从最开始读取数据。

    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

下面看一下get函数,get函数的实现也是在子类HeapByteBuffer中,nextGetIndex函数实在鸡肋Buffer中实现的,主要功能就是get之后的pos后移工作。Bits.getInt和前面的Bits.putInt相似,不错过多介绍。

    public int getInt() {
        return Bits.getInt(this, ix(nextGetIndex(4)), bigEndian);
    }

    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }

至此 讲的差不多了。一些函数开头的判断没有详细的去讲,他们的主要工作就是在put和get的时候越界的异常抛出。
在看源码的时候发现了另一个函数,这个函数很有意思public int getInt(int i) 从字面上看上去好像是获取第i个Int,调用一下试试,看看疗效。

    public int getInt(int i) {
        return Bits.getInt(this, ix(checkIndex(i, 4)), bigEndian);
    }

	static public void asIntBuffer() {
		ByteBuffer bBuf = ByteBuffer.allocate(512);
		bBuf.putInt(1);
		bBuf.putInt(2);
		bBuf.putInt(3);
		bBuf.putInt(4);
		bBuf.putInt(5);
		bBuf.putInt(6);
		bBuf.putInt(7);
		bBuf.flip();
		System.out.println("缓冲区Pos:" + bBuf.position() + "  缓冲区Limit:"
				+ bBuf.limit());
		 for (int i = 0; i < 7; i++) {
			 System.out.println(bBuf.getInt(i));
		 }
	}
对应输出:
缓冲区Pos:0  缓冲区Limit:28
1
256
65536
16777216
2
512
131072

这时候机会发现,他并没有像我们想想的那样去工作,其中256,65536是怎么来的呢。继续看public int getInt(int i) 的源码。发现它和之前分getInt唯一不同的就是在checkIndex(4) 通过看 final int checkIndex(int i, int nb) 的源码发现,该函数什么都没做只是check了一下limit。那256优势怎么来的呢?

    final int checkIndex(int i, int nb) {               // package-private
        if ((i < 0) || (nb > limit - i))
            throw new IndexOutOfBoundsException();
        return i;
    }

下面开一下Bits.getInt()和 getIntB()以及makeInt() 的源码,我们能够知道,当我们要获取第i个位置的int时,也就是bi。此时bi并没有跳过4个字节,而是在Buffer数组总按照我们提供的i去取了i之后的三个字节,在加上第i个构成了一个4字节的int。

    static int getInt(ByteBuffer bb, int bi, boolean bigEndian) {
        return bigEndian ? getIntB(bb, bi) : getIntL(bb, bi) ;
    }
    static int getIntB(ByteBuffer bb, int bi) {
        return makeInt(bb._get(bi    ),
                       bb._get(bi + 1),
                       bb._get(bi + 2),
                       bb._get(bi + 3));
    }
    static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
        return (((b3       ) << 24) |
                ((b2 & 0xff) << 16) |
                ((b1 & 0xff) <<  8) |
                ((b0 & 0xff)      ));
    }

至于256怎么来的?下面分析一下,Buffer是一个字节数组。当我们putInt(1)putInt(2) 之后,里面的前8个字节数组是这个样子的(大端存储)

16进制
00 00 00 01 00 00 00 02 
转成2进制
00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000002 

当我们取调用getInt(2) 时其实取出来的是包括第二个字节以及后面的三个,也就是00000000 00000000 00000001 00000000 也就是256, 后面的数字同理。

当我们知道getInt(i) 后我们在来看一下putInt(index,i); 看似是在第index位置插入Int值i,其实不然

	static public void asIntBuffer() {
		ByteBuffer bBuf = ByteBuffer.allocate(512);
		for (int i = 0; i < 10; i++) {
			bBuf.putInt(i, i);
		}
		System.out.println("缓冲区Pos:" + bBuf.position() + "  缓冲区Limit:"
				+ bBuf.limit());

	}
输出:缓冲区Pos:0  缓冲区Limit:512

我们发现,pos压根没有移动,Buffer中压根没数据。同getInt(i) 类似pputInt(inex,i) 同样没引起pos的移动,pos始终处于0的位置,在我们get数据时,在nextGetIndex() 函数校验时就抛出异常了,总上,使用putInt(index,i) 必须在index位置有数据的情况下使用。

    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/186644.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 多重共线性:python中利用statsmodels计算VIF和相关系数消除共线性

    多重共线性:python中利用statsmodels计算VIF和相关系数消除共线性多重共线性在python中的解决方法本文将讨论多重共线性的相关概念及利用python自动化消除多重共线性的方法,以供参考,欢迎拍砖线性模型与非线性模型关于线性模型与非线性模型的定义,似乎并没有确切的定论,但是个人认为建模首先得清楚地认识样本,样本有线性可分与线性不可分两种,所谓是否线性可分,是指是否存在一条直线(或平面)将样本分开。上图中y=0和y=1的样本可以由一条直线分开,如逻辑回归…

  • 哈希表(散列表)原理详解

    哈希表(散列表)原理详解什么是哈希表?哈希表(Hashtable,也叫散列表),是根据关键码值(Keyvalue)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。记录的存储位置=f(关键字)这里的对应关系f称为散列函数,又称为哈希(Hash函数),采用散列技术将记录存储在一块连续的存储空间中,这块…

  • windowsform和wpf(winform和wpf我选哪个)

    WPF开发于WinForm之后,从技术发展的角度,WPF比WinForm先进是不容置疑的。我觉得WPF相比于WinForm有下面的一些较好的特性:解决WindowHandle问题在WindowsGDI或WinForm开发中复杂的GUI应用程序,会使用的大量的控件,如Grid等。而每个控件或Gridcell都是一个小窗口,会使用一个Windowhandle,尽管控件厂商提供了很多优化…

  • 为什么有些人除了上课时间以外都没有学习,成绩却还是很好?

    为什么有些人除了上课时间以外都没有学习,成绩却还是很好?新高二学生,成绩在班级10~12名左右。有两个女生特别6。第一个,我室友,上课以外的时间都是看漫画,看小说,画画,睡觉,吃零食。但是成绩就是很好,她化学进了学校的竞赛培训班,数学也很好,但是平时完全没有死命学的迹象。第二个,另一个寝室的,我们是寄宿学校要求交手机,她没交,经常看见她玩,成绩也是铁打的特别好。我观察了一下,她们共同特点就是上课听讲特别认真,然后就是会玩。我真的很想

  • 杂谈 – 自定义搜索引擎

    杂谈 – 自定义搜索引擎在Firefox上,以{肯定被驳回}搜索引擎为例。{肯定被驳回}搜索引擎搜索英文关键词时,给出的也基本是英文,例如这样:看了几篇不符合自己的胃口,遂点击了左上角小提示:仅限简体中文结果,例如这样:发现第二篇就是自己想要的东西,遂想,中文的结果也不错,何不设置不管中英文关键字,都先输出中文呢?所以实现方式,就是:添加addcustomsearchengine。2.点击扩展图标其中,SearchURL为:为了避免被驳回,请自行输入。然后点击蓝色按钮。然后在Firefox的

  • 收藏!!「自然语言处理(NLP)」全球学术界知名学者教授信息大盘点(全)!

    收藏!!「自然语言处理(NLP)」全球学术界知名学者教授信息大盘点(全)!来源:AINLPer微信公众号(点击了解一下吧)编辑:ShuYini校稿:ShuYini时间:2020-1-13引言    人工智能发展迅速,近几年自然语言处理已经成为热门研究方向,根据这些会议和期刊上近10年发表论文的引用情况(根据GoolgeScholar)生成了高引学者列表。供大家参考学习。1、RyanT.Mcdonald【Google】个人主页:https…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号