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