Каналы Java NIO предоставляют важную новую возможность, известную как scatter/gather(в некоторых кругах именуемую vectored I/O). Scatter/gather — это простая, но мощная концепция.
Scatter/gather — это метод, с помощью которого байты могут быть считаны из потока в набор буферов(векторов) с помощью одного вызова read(), а байты могут быть записаны из набора буферов в поток с помощью одного вызова write().
Большинство современных операционных систем поддерживают собственный векторный ввод-вывод. Когда вы запрашиваете операцию scatter/gather на канале, запрос будет преобразован в соответствующие собственные вызовы для непосредственного заполнения или опустошения буферов. Это большой выигрыш, поскольку копирование буферов и системные вызовы сокращаются или устраняются.
До недавнего времени Java не имела возможности выполнять операции векторного ввода-вывода. Поэтому мы привыкли читать байты напрямую в один массив байтов или выполнять несколько чтений для извлечения данных.
1. API-интерфейсы Scatter/Gather
Рассеивающее чтение из канала — это операция чтения, которая считывает данные в более чем один буфер. Таким образом, канал «рассеивает» данные из канала в несколько буферов.
Сбор записи в канал — это операция записи, которая записывает данные из более чем одного буфера в один канал. Таким образом, канал «собирает» данные из нескольких буферов в один канал.
Рассеивание и сбор данных могут быть действительно полезны в ситуациях, когда вам необходимо работать с различными частями передаваемых данных по отдельности.
public interface ScatteringByteChannel extends ReadableByteChannel{public long read(ByteBuffer [] dsts) throws IOException;public long read(ByteBuffer [] dsts, int offset, int length) throws IOException;}public interface GatheringByteChannel extends WritableByteChannel{public long write(ByteBuffer[] srcs) throws IOException;public long write(ByteBuffer[] srcs, int offset, int length) throws IOException;}
Вы можете видеть, что каждый интерфейс добавляет два новых метода, которые принимают массив буферов в качестве аргументов.
Теперь давайте напишем небольшой пример, чтобы понять, как использовать эту функцию.
2. Пример Java Scatter/Gather IO
В этом примере я создал два буфера. Один буфер будет хранить случайное число, а другой — случайную строку. Я буду использовать GatheringByteChannel для чтения-записи данных, хранящихся в обоих буферах в файловом канале.
Затем я прочитаю данные из файла в два отдельных буфера с помощью ScatteringByteChannel и выведу содержимое в консоль, чтобы убедиться, что сохраненные и извлеченные данные совпадают.
import java.io.FileInputStream;import java.io.FileOutputStream;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.nio.channels.GatheringByteChannel;import java.nio.channels.ScatteringByteChannel;public class ScatteringAndGatheringIOExample{public static void main(String params[]){String data = "Scattering and Gathering example shown in howtodoinjava.com";gatherBytes(data);scatterBytes();}/** gatherBytes() reads bytes from different buffers and writes to file* channel. Note that it uses a single write for both the buffers.*/public static void gatherBytes(String data){//First Buffer holds a random numberByteBuffer bufferOne = ByteBuffer.allocate(4);//Second Buffer holds data we want to writeByteBuffer buffer2 = ByteBuffer.allocate(200);//Writing Data sets to BufferbufferOne.asIntBuffer().put(13);buffer2.asCharBuffer().put(data);//Calls FileOutputStream(file).getChannel()GatheringByteChannel gatherer = createChannelInstance("test.txt", true);//Write data to filetry{gatherer.write(new ByteBuffer[] { bufferOne, buffer2 });}catch(Exception e){e.printStackTrace();}}/** scatterBytes() read bytes from a file channel into a set of buffers. Note that* it uses a single read for both the buffers.*/public static void scatterBytes(){//First Buffer holds a random numberByteBuffer bufferOne = ByteBuffer.allocate(4);//Second Buffer holds data we want to writeByteBuffer bufferTwo = ByteBuffer.allocate(200);//Calls FileInputStream(file).getChannel()ScatteringByteChannel scatterer = createChannelInstance("test.txt", false);try{//Reading from the channelscatterer.read(new ByteBuffer[] { bufferOne, bufferTwo });}catch(Exception e){e.printStackTrace();}//Read the buffers seperatelybufferOne.rewind();bufferTwo.rewind();int bufferOneContent = bufferOne.asIntBuffer().get();String bufferTwoContent = bufferTwo.asCharBuffer().toString();//Verify the contentSystem.out.println(bufferOneContent);System.out.println(bufferTwoContent);}public static FileChannel createChannelInstance(String file, boolean isOutput){FileChannel fc = null;try{if(isOutput) {fc = new FileOutputStream(file).getChannel();} else {fc = new FileInputStream(file).getChannel();}}catch(Exception e) {e.printStackTrace();}return fc;}}
3. Заключение
Scatter/gather может быть чрезвычайно мощным инструментом при правильном использовании. Он позволяет делегировать операционной системе тяжелую работу по разделению считываемых данных на несколько сегментов или сбору разрозненных фрагментов данных в единое целое. Это может быть огромным выигрышем, поскольку операционная система высоко оптимизирована для такого рода вещей.
Это экономит вам работу по перемещению вещей, тем самым избегая копий буфера, и уменьшает объем кода, который вам нужно написать и отладить. Поскольку вы в основном собираете данные, предоставляя ссылки на контейнеры данных, различные фрагменты могут быть собраны разными способами, создавая несколько массивов ссылок на буферы в разных комбинациях.