【杂记】java 读取未知文件大小,实现byte[]数组的动态扩容

小红:小明我这边有一个位置大小文件,需要导入到一个byte数组中,但是自己定义的byte数组如果太小,可能导入的文件不全,会丢失部分数据,如果定义的数组大小太大,会导致内存的浪费,我该如何处理?
小明:对于这种未知文件大小的读取,我们需要在读取的时候,对数组进行动态的扩容,以确保文件能完整的读入。
小红:那该如何做?
小明:
总体的思路如下:

  1. 我们应该先对数组设置一个初始化的大小值A,变量B来记录流当前的读取位置,C表示我们定义的buffer的最大值,D表示buffer的固定值(C>D);
  2. 我们需要一个死循环,将每一次将流读取的位置赋值于B;
  3. 我们比较B的值,当流读取完之后,B的值就回小于0,这是我们就知道流以及读完,退出死循环为止;如果流在每一次循环过程中发现都没有读完,那这时我们就需要对当前数组进行比较,看是否扩容;
  4. 我们需要先比较A与C-A的大小,为什么这样做?主要是我们需要确保我们初始化的值A*2是否比我们自定义buffer的最大值大,为我们二次扩容留下足够的大小空间;
  5. 当2A大于C时,我们需要比较A是否大于C,如果A大于C我们就需要抛出异常,告诉程序以及超出自定义的内存,否则就直接将最大值bufferC赋值于A;
  6. 当2A小于C时,表示当前初始化的数组大小还是可以扩容的,我们将A进行<<1处理,并且将处理后的值与D比较,选出最大值,作为A的新值;
  7. 此时我们已经对新的数组完成了初始化大小的确认,下面就是进行copy处理;
  8. Arrays.copy这个方法进行copy,通过源码我们可以发现,在copy的过程中,主要是new出来一个新的bety数组,将目标数据进行copy,其中主要的是新的数组copy的长度是,目标数组的大小和A的最小值;
  9. 退出死循环后,我们只需要比较一下B与A是否一致,如果一致就返回当前数组,如果不一致,我们就将数据进行copy,copy的数组长度时流最后读出的位置。
    这就是我对数组动态扩容的思路。

代码源码来源于Files.readAll()方法


    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
    private static final int BUFFER_SIZE = 8192;

    /**
     * 该方法主要是对未知文件大小的流,实现动态扩展byte[]大小
     *
     * @param source      文件输入流
     * @param initialSize byte[]数组初始化大小
     * @return 返回文件流的byte[]
     * @throws IOException 流读取会产生的IO异常
     */
    public static byte[] read(InputStream source, int initialSize) throws IOException {
        int capacity = initialSize;
        //①定义一个初始化的byte[]
        byte[] buf = new byte[capacity];
        //②流读取到byte[]的位置
        int nread = 0;
        int n;
        //③死循环,读流
        for (; ; ) {
            //④将当前流读到初始化的byte[]中,read参数(读取的byte[],byte[]的起始位置,读取的长度),返回的当前读取的流的位置
            while ((n = source.read(buf, nread, capacity - nread)) > 0) {
                //⑤将当前流读取的位置,赋值与流读取的byte[]的位置
                nread += n;
            }
            //⑥判断n当前流的位置,当n小于0时或者流读取的位置小于0时,表示流以及读完,退出当前死循环
            if (n < 0 || (n = source.read()) < 0) break;
            //⑦判断当byte[]的大小,当前值小于等于(最大byte的buffer的值-当前byte[]的大小)⑪⑫⑬⑭⑮⑯
            if (capacity <= MAX_BUFFER_SIZE - capacity) {
                //初始值重新赋值,在当前值二进制值向左移一位返回值和我们自己定义的buffer的大小之前选择最大的值
                capacity = Math.max(capacity << 1, BUFFER_SIZE);
            } else {
                //⑧当前byte[]的大小值,大于(最大byte的buffer的值-当前byte[]的大小)
                //⑨先判断当前值是否等于最大byte的buffer的值,等于就抛出内存溢出异常,结束当前进程
                if (capacity == MAX_BUFFER_SIZE){
                    throw new OutOfMemoryError("Required array size too large");
                }
                //⑩否则将最大值赋予byte[]的大小的值
                capacity = MAX_BUFFER_SIZE;
            }
            //⑪将当前byte[]数组进行copy,copy参数(需要copy的byte[],初始化的大小)
            //源码里面查看,在数组copy的过程中,会在出现的大小值和需要copy数组的大小值进行最小选取,选取最小值为返回数组的大小
            buf = Arrays.copyOf(buf, capacity);
            //⑫在数组的末尾添加最后结束符
            buf[nread++] = (byte) n;
        }
        //⑬判断最终的数组的大小是否和流读取的最后位置一样,如果一致返回当前数组,否则返回copy的新数组
        return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值