原文连接:http://tutorials.jenkov.com/java-performance/resizable-array.html,若有侵权,立删html
有时候咱们为了快速便捷的读取数据,想把数据保存在单个连续的数组里。这就须要数组是能够调节大小的,或者是能够扩张的。可是在Java中数组的大小是不能够调节的。你必须亲自实现一个可调节大小的数组,在本文中我将展现如何实现这个数组。java
源代码哦:https://github.com/jjenkov/java-resizable-arraygit
包含了三个classes和两个测试类github
想象下若是你有一个接受不一样数据的Server,最小的数据4KB,大的1MB或者更大。数组
若是Server同一时间有1000K+的访问链接,咱们须要知道要为数据提早分配多大的空间。咱们不能仅仅只是分配一个最大的空间(1MB&16MB)为每一个buffer。当有一千条链接访问Server时(100.000 × 1MB = 100GB),须要100GB的空间。测试
换一种思路,咱们先从最小的信息开始处理,若是信息size超过了最小信息,那么咱们从新分配一块空间并把原来的信息复制过去。若是信息又超过了现有的容量,那么再次分配并复制spa
使用这种策略,大多数信息将会使用最小buffer。这也就意味着Server的内存使用会变得很是高效,(100.000×4KB = 400MB),即便链接访问达到(1000.000 × 4KB = 4GB)。大多数Server也能hold住。code
resizable array 包含两个部分orm
ResizableArrayBuffer包含一个单一,高容的数组。这个数组被分为三个部分。一个部分为small arrays,一个为medium size arrays 和 large array。htm
ResizableArray就表示单一,高容的数组,它的数据存储在ResizableArrayBuffer中的数组。
有图有真相
为了在ResizableArrayBuffer中的large array为各个大小不一的message预留空间,咱们须要保证array不会被任意大小的message填充满。例如,接受了大量的small mesages,并不会占用所有的内存,可是却会阻塞Server接受medium和large message。类似的,接受大量的large或medium message也不会占用全部的内存,但却会使得另外连个message type产生阻塞。
假如全部的message都是从small messages开始,若是大量的small arrays被耗尽,不管medium array或large array是否有空间,都不会分配新的array。可是若是将small arrays的大小设置的合理,这种状况发生的几率就会下降。
即使small message部分所有被占用了,small message仍是有可能会grow为medium and large messages的。
large array 在ResizableArrayBuffer中被分为三个部分,每一个部分又被分为不少小块。每一个小块在每一个部分中有着相同的大小。
当全部的块在其所处的部分有着相同的大小时,它是很容易去跟踪那些使用着的块和没有使用的块。你可使用一个queue(队列)来保存每一个块的开始索引。对于每一个部分都须要有这么一个queue(队列),一个queue(队列)去跟踪可用的small blocks,medium blocks 和 large blocks 一样是这样的。
Allocating a block from any of the sections can be accomplished simply by taking the next free block start index from the queue associated with the desired section. Freeing a block is done by putting the start index back into the corresponding queue.(能力有限)
对于queue(队列)我曾经用一个简单的Ring Buffer 实现过,连接:http://tutorials.jenkov.com/java-performance/ring-buffer.html
当你向array中写入数据时,Resizable array将会本身expand。若是你尝试向其中写入更多的数据,可是 array 没有多余的空间,它会被分配一个新的更大的 block 而且把原有的数据所有复制过去,以前的使用的block就free掉。
一旦你得到了一个更大的array,你就应该释放掉原来的array,以便它能够存储别的message。
Using the ResizableArrayBuffer
我教你如何使用ResizableArrayBuffer
Creating a ResizableArrayBuffer
首先你必须建立一个ResizableArrayBuffer
1 int smallBlockSize = 4 * 1024; 2 int mediumBlockSize = 128 * 1024; 3 int largeBlockSize = 1024 * 1024; 4 5 int smallBlockCount = 1024; 6 int mediumBlockCount = 32; 7 int largeBlockCount = 4; 8 9 ResizableArrayBuffer arrayBuffer = 10 new ResizableArrayBuffer( 11 smallBlockSize , smallBlockCount, 12 mediumBlockSize, mediumBlockCount, 13 largeBlockSize, largeBlockCount);
This example creates a ResizableArrayBuffer
with a small array size of 4KB, medium array size of 128KB and a larger array size of 1MB. The ResizableArrayBuffer
contains space for 1024 small arrays (4MB in total), 32 medium arrays (4MB in total) and 4 large arrays (4MB in total) - for a full shared array size of 12MB.(不想翻了)
得到一个ResizableArray instance,调用ResizableArrayBuffer`s getArray()方法。
ResizableArray resizableArray = arrayBuffer.getArray();
这个例子会得到一个很小的ResizableArray(4KB)
Writing to a ResizableArray
你能够调用ResizableArray本身的write()方法。你能够重构它
1 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 2 3 for(int i=0; i < 1024; i++){ 4 byteBuffer.put((byte) i); 5 } 6 byteBuffer.flip(): 7 8 int bytesCopied = resizableArray.write(byteBuffer);
这个例子将ByteBuffer中的数据copy到ResizableArray中的array(block),返回值是复制了多少个字节数。
一种状况是ByteBuffer中的数据大于ResizableArray的容量,那么ResizableArray将尝试expand自身的空间为了ByteBuffer中的数据。若是ResizableArray在expanding后不能包含全部的ByteBuffer数据,write()方法将会返回-1而且也不会复制data。
当你从ResizableArray读取数据时,你能够直接在全部ResizableArray实例共享的共享数组中读取。ResizableArray包含如下几个字段:
1 public byte[] sharedArray = null; 2 public int offset = 0; 3 public int capacity = 0; 4 public int length = 0;
shareArray字段引用全部的ResizableArray实例中的共享array,这是保存在ResizableArrayBuffer中的内部array。
offset字段包含着共享array中的开始索引。
capacity字段包含ResizableArray实例中分配的共享数组中block的size。
length字段包含多少正在使用的block
去读取卸载ResizableArray中的数据时,是从shareArray[offset]到shareArray[offset+length-1]。
一旦你使用完了ResizableArray,你就要free它。仅须要调用ResizableArray中的free()方法便可。
resizableArray.free();
调用free()方法负责返回使用过的block到正确的queue,无论分配给ResizableArray中block的大小。