Digamos que tengo enorme archivo de registro del servidor web que no encaja en la memoria. Necesito para transmitir este archivo a un método mapreduce y guardar en la base de datos. Lo hago uso de Java 8 api corriente. Por ejemplo, puedo obtener una lista después del proceso de mapreduce tales como, el consumo por el cliente, el consumo a través de IP, el consumo de los contenidos. Pero, mis necesidades no son que al igual que dado en mi ejemplo. Ya que no puedo compartir el código, sólo quiero dar ejemplo básico.
Por Java 8 Corriente del Api, quiero leer el archivo exactamente una vez, obtener 3 listas al mismo tiempo, mientras yo estoy Transmisión de archivos, paralelo o secuencial. Pero en paralelo sería bueno. ¿Hay alguna forma de hacer eso?
He adaptado la respuesta a esta pregunta a su caso. El Spliterator personalizada va a "dividir" la corriente en múltiples flujos que recogen por diferentes propiedades:
@SafeVarargs
public static <T> long streamForked(Stream<T> source, Consumer<Stream<T>>... consumers)
{
return StreamSupport.stream(new ForkingSpliterator<>(source, consumers), false).count();
}
public static class ForkingSpliterator<T>
extends AbstractSpliterator<T>
{
private Spliterator<T> sourceSpliterator;
private List<BlockingQueue<T>> queues = new ArrayList<>();
private boolean sourceDone;
@SafeVarargs
private ForkingSpliterator(Stream<T> source, Consumer<Stream<T>>... consumers)
{
super(Long.MAX_VALUE, 0);
sourceSpliterator = source.spliterator();
for (Consumer<Stream<T>> fork : consumers)
{
LinkedBlockingQueue<T> queue = new LinkedBlockingQueue<>();
queues.add(queue);
new Thread(() -> fork.accept(StreamSupport.stream(new ForkedConsumer(queue), false))).start();
}
}
@Override
public boolean tryAdvance(Consumer<? super T> action)
{
sourceDone = !sourceSpliterator.tryAdvance(t -> queues.forEach(queue -> queue.offer(t)));
return !sourceDone;
}
private class ForkedConsumer
extends AbstractSpliterator<T>
{
private BlockingQueue<T> queue;
private ForkedConsumer(BlockingQueue<T> queue)
{
super(Long.MAX_VALUE, 0);
this.queue = queue;
}
@Override
public boolean tryAdvance(Consumer<? super T> action)
{
while (queue.peek() == null)
{
if (sourceDone)
{
// element is null, and there won't be no more, so "terminate" this sub stream
return false;
}
}
// push to consumer pipeline
action.accept(queue.poll());
return true;
}
}
}
Se puede utilizar de la siguiente manera:
streamForked(Stream.of(new Row("content1", "client1", "location1", 1),
new Row("content2", "client1", "location1", 2),
new Row("content1", "client1", "location2", 3),
new Row("content2", "client2", "location2", 4),
new Row("content1", "client2", "location2", 5)),
rows -> System.out.println(rows.collect(Collectors.groupingBy(Row::getClient,
Collectors.groupingBy(Row::getContent,
Collectors.summingInt(Row::getConsumption))))),
rows -> System.out.println(rows.collect(Collectors.groupingBy(Row::getClient,
Collectors.groupingBy(Row::getLocation,
Collectors.summingInt(Row::getConsumption))))),
rows -> System.out.println(rows.collect(Collectors.groupingBy(Row::getContent,
Collectors.groupingBy(Row::getLocation,
Collectors.summingInt(Row::getConsumption))))));
// Output
// {client2={location2=9}, client1={location1=3, location2=3}}
// {client2={content2=4, content1=5}, client1={content2=2, content1=4}}
// {content2={location1=2, location2=4}, content1={location1=1, location2=8}}
Observe que puede hacer casi cualquier cosa que quiera con sus copias de la corriente. Como por su ejemplo, he usado un apilado groupingBy
colector para agrupar las filas por dos propiedades y luego resumió la propiedad int. Así, el resultado será una Map<String, Map<String, Integer>>
. Pero también se puede utilizar para otros escenarios:
rows -> System.out.println(rows.count())
rows -> rows.forEach(row -> System.out.println(row))
rows -> System.out.println(rows.anyMatch(row -> row.getConsumption() > 3))