ব্যাচ প্রসেসিং সহ জাভা 8 স্ট্রিম


101

আমার কাছে একটি বড় ফাইল রয়েছে যাতে আইটেমের একটি তালিকা রয়েছে।

আমি আইটেমগুলির একটি ব্যাচ তৈরি করতে চাই, এই ব্যাচটি দিয়ে একটি এইচটিটিপি অনুরোধ করব (এইচটিটিপি অনুরোধে আইটেমগুলির মধ্যে সমস্ত পরামিতি হিসাবে প্রয়োজন)। আমি এটি একটি forলুপ দিয়ে খুব সহজেই করতে পারি , তবে জাভা 8 প্রেমিক হিসাবে, আমি জাভা 8 এর স্ট্রিম ফ্রেমওয়ার্ক (এবং অলস প্রক্রিয়াকরণের সুবিধাগুলি দিয়ে) দিয়ে এটি লেখার চেষ্টা করতে চাই।

উদাহরণ:

List<String> batch = new ArrayList<>(BATCH_SIZE);
for (int i = 0; i < data.size(); i++) {
  batch.add(data.get(i));
  if (batch.size() == BATCH_SIZE) process(batch);
}

if (batch.size() > 0) process(batch);

আমি দীর্ঘ লাইন কিছু করতে চাই lazyFileStream.group(500).map(processBatch).collect(toList())

এই কাজ করতে সবচেয়ে ভালো উপায় কি হতে পারে?


আমি কীভাবে গ্রুপিংটি সম্পাদন করব তা বেশ বুঝতে পারছি না, তবে ফাইল # লাইনগুলি আলস্যভাবে ফাইলের বিষয়বস্তু পড়বে।
টবি

4
সুতরাং আপনার মূলত একটি বিপরীতমুখী flatMap(+ স্ট্রিমগুলি আবার ধসের জন্য একটি অতিরিক্ত ফ্ল্যাটম্যাপ) প্রয়োজন? আমি মনে করি না স্ট্যান্ডার্ড লাইব্রেরিতে সুবিধাজনক পদ্ধতি হিসাবে এর মতো কিছু রয়েছে। হয় আপনাকে একটি তৃতীয় পক্ষের lib সন্ধান করতে হবে বা স্প্লিটেটর এবং / অথবা কোনও সংগ্রাহকের উপর ভিত্তি করে আপনার নিজস্ব লিখন লিখতে হবে স্রোতের স্রোত
নির্বাহকারী

4
হতে পারে আপনি এবং এর Stream.generateসাথে একত্রিত করতে পারেন , তবে সমস্যাটি হল ব্যতিক্রমগুলি সহ স্ট্রিমগুলি ভাল হয় না। এছাড়াও, এটি সম্ভবত সমান্তরালভাবে ভাল নয়। আমি মনে করি লুপটি এখনও সেরা বিকল্প। reader::readLinelimitfor
tobias_k

আমি সবেমাত্র একটি উদাহরণ কোড যুক্ত করেছি। আমার মনে হয় না ফ্ল্যাটম্যাপটি যাওয়ার উপায়। সন্দেহ হচ্ছে যে আমাকে একটি কাস্টম স্প্লিটেটর লিখতে হতে পারে
অ্যান্ডি ডাং

4
আমি এই জাতীয় প্রশ্নের জন্য "স্ট্রিম অপব্যবহার" শব্দটি তৈরি করছি।
কোর্ভিন

উত্তর:


13

বিঃদ্রঃ! এই দ্রষ্টব্যটি forEach চালানোর আগে পুরো ফাইলটি পড়ে।

আপনি এটি JOOλ দিয়ে করতে পারেন , একটি পাঠাগার যা জাভা 8 স্ট্রিমগুলিকে একক থ্রেডযুক্ত , ক্রমযুক্ত প্রবাহের ব্যবহারের ক্ষেত্রে প্রসারিত করে:

Seq.seq(lazyFileStream)              // Seq<String>
   .zipWithIndex()                   // Seq<Tuple2<String, Long>>
   .groupBy(tuple -> tuple.v2 / 500) // Map<Long, List<String>>
   .forEach((index, batch) -> {
       process(batch);
   });

পর্দার আড়ালে zipWithIndex()ঠিক:

static <T> Seq<Tuple2<T, Long>> zipWithIndex(Stream<T> stream) {
    final Iterator<T> it = stream.iterator();

    class ZipWithIndex implements Iterator<Tuple2<T, Long>> {
        long index;

        @Override
        public boolean hasNext() {
            return it.hasNext();
        }

        @Override
        public Tuple2<T, Long> next() {
            return tuple(it.next(), index++);
        }
    }

    return seq(new ZipWithIndex());
}

... যদিও এর groupBy()জন্য এপিআই সুবিধা:

default <K> Map<K, List<T>> groupBy(Function<? super T, ? extends K> classifier) {
    return collect(Collectors.groupingBy(classifier));
}

(অস্বীকৃতি: আমি JOOλ এর পিছনে সংস্থার পক্ষে কাজ করি)


কি দারুন. এটি ঠিক আমি যা খুঁজছি is আমাদের সিস্টেমটি সাধারণত ডেটা স্ট্রিমগুলি ক্রমানুসারে প্রসেস করে তাই জাভা 8 এ যাওয়ার জন্য এটি উপযুক্ত fit
অ্যান্ডি ডাং

16
নোট করুন যে এই সমাধানটি Map
অবিচ্ছিন্নভাবে

129

সম্পূর্ণতার জন্য, এখানে একটি পেয়ারা সমাধান।

Iterators.partition(stream.iterator(), batchSize).forEachRemaining(this::process);

প্রশ্নে সংগ্রহটি উপলভ্য যাতে কোনও স্ট্রিমের প্রয়োজন হয় না এবং এটি এ হিসাবে লেখা যেতে পারে,

Iterables.partition(data, batchSize).forEach(this::process);

11
Lists.partitionআরেকটি প্রকরণ যা আমার উল্লেখ করা উচিত ছিল।
বেন ম্যানস

4
এই অলস, তাই না? এটি Streamপ্রাসঙ্গিক ব্যাচ প্রক্রিয়া করার আগে
পুরোটিকে

4
@ ওরিরাব হ্যাঁ এটি ব্যাচের মধ্যে অলস, যেমন এটি batchSizeপুনরাবৃত্তির জন্য উপাদান গ্রহণ করবে ।
বেন ম্যানস

আপনি কটাক্ষপাত দয়া করে গেল stackoverflow.com/questions/58666190/...
gstackoverflow

62

খাঁটি জাভা -8 বাস্তবায়নও সম্ভব:

int BATCH = 500;
IntStream.range(0, (data.size()+BATCH-1)/BATCH)
         .mapToObj(i -> data.subList(i*BATCH, Math.min(data.size(), (i+1)*BATCH)))
         .forEach(batch -> process(batch));

নোট করুন যে JOOl এর বিপরীতে এটি সমান্তরালে খুব সুন্দরভাবে কাজ করতে পারে (আপনি dataযদি একটি এলোমেলো অ্যাক্সেসের তালিকা হয়ে থাকেন তবে)।


4
যদি আপনার ডেটা আসলে একটি স্ট্রিম হয়? (একটি ফাইলের লাইনে বা নেটওয়ার্ক থেকে বলি) lets
ওমরি ইয়াদান

7
@OmryYadan, প্রশ্ন থেকে ইনপুট থাকার সম্পর্কে ছিল List(দেখুন data.size(), data.get()প্রশ্নে)। আমি জিজ্ঞাসা প্রশ্নের উত্তর দিচ্ছি। আপনার যদি অন্য প্রশ্ন থাকে তবে পরিবর্তে এটি জিজ্ঞাসা করুন (যদিও আমি মনে করি স্ট্রিমের প্রশ্নটি ইতিমধ্যে জিজ্ঞাসা করা হয়েছিল)।
তাগীর ভালিভ

4
সমান্তরালভাবে ব্যাচগুলি কীভাবে প্রসেস করবেন?
স্যুপ_বয়

38

খাঁটি জাভা 8 সমাধান :

এটি মার্জিতভাবে এটি করার জন্য আমরা একটি কাস্টম সংগ্রাহক তৈরি করতে পারি, যা প্রতিটি ব্যাচের প্রক্রিয়া করার জন্য একটি batch sizeএবং একটি Consumerগ্রহণ করে:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.*;
import java.util.stream.Collector;

import static java.util.Objects.requireNonNull;


/**
 * Collects elements in the stream and calls the supplied batch processor
 * after the configured batch size is reached.
 *
 * In case of a parallel stream, the batch processor may be called with
 * elements less than the batch size.
 *
 * The elements are not kept in memory, and the final result will be an
 * empty list.
 *
 * @param <T> Type of the elements being collected
 */
class BatchCollector<T> implements Collector<T, List<T>, List<T>> {

    private final int batchSize;
    private final Consumer<List<T>> batchProcessor;


    /**
     * Constructs the batch collector
     *
     * @param batchSize the batch size after which the batchProcessor should be called
     * @param batchProcessor the batch processor which accepts batches of records to process
     */
    BatchCollector(int batchSize, Consumer<List<T>> batchProcessor) {
        batchProcessor = requireNonNull(batchProcessor);

        this.batchSize = batchSize;
        this.batchProcessor = batchProcessor;
    }

    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }

    public BiConsumer<List<T>, T> accumulator() {
        return (ts, t) -> {
            ts.add(t);
            if (ts.size() >= batchSize) {
                batchProcessor.accept(ts);
                ts.clear();
            }
        };
    }

    public BinaryOperator<List<T>> combiner() {
        return (ts, ots) -> {
            // process each parallel list without checking for batch size
            // avoids adding all elements of one to another
            // can be modified if a strict batching mode is required
            batchProcessor.accept(ts);
            batchProcessor.accept(ots);
            return Collections.emptyList();
        };
    }

    public Function<List<T>, List<T>> finisher() {
        return ts -> {
            batchProcessor.accept(ts);
            return Collections.emptyList();
        };
    }

    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
}

Ptionচ্ছিকভাবে তারপরে একটি সহায়ক ইউটিলিটি শ্রেণি তৈরি করুন:

import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collector;

public class StreamUtils {

    /**
     * Creates a new batch collector
     * @param batchSize the batch size after which the batchProcessor should be called
     * @param batchProcessor the batch processor which accepts batches of records to process
     * @param <T> the type of elements being processed
     * @return a batch collector instance
     */
    public static <T> Collector<T, List<T>, List<T>> batchCollector(int batchSize, Consumer<List<T>> batchProcessor) {
        return new BatchCollector<T>(batchSize, batchProcessor);
    }
}

ব্যবহারের উদাহরণ:

List<Integer> input = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> output = new ArrayList<>();

int batchSize = 3;
Consumer<List<Integer>> batchProcessor = xs -> output.addAll(xs);

input.stream()
     .collect(StreamUtils.batchCollector(batchSize, batchProcessor));

আমি আমার কোডটি গিটহাবটিতেও পোস্ট করেছি, যদি কেউ নজর দিতে চান:

গিথুব লিঙ্ক


4
আপনি যদি আপনার স্ট্রিম থেকে সমস্ত উপাদান মেমরির সাথে ফিট করতে না পারেন তবে এটি একটি ভাল সমাধান। এছাড়াও এটি অন্তহীন স্ট্রিমগুলিতে কাজ করবে না - সংগ্রহের পদ্ধতিটি টার্মিনাল, যার অর্থ ব্যাচগুলির প্রবাহ উত্পাদন করার চেয়ে এটি স্ট্রিমটি সমাপ্ত না হওয়া পর্যন্ত অপেক্ষা করবে এবং তারপরে ব্যাচগুলিতে ফলাফল প্রক্রিয়া করবে।
অ্যালেক্স অ্যাকারম্যান

4
@ অ্যালেক্সআ্যাকারম্যান একটি অসীম স্ট্রিমের অর্থ হবে ফিনিশার কখনই ডাকা হয় না, তবে সংগ্রহকারীটিকে এখনও বলা হবে যাতে আইটেমগুলি এখনও প্রক্রিয়াজাত করা হবে। এছাড়াও, এটি কেবলমাত্র একবারে স্মৃতিতে থাকা আইটেমগুলির ব্যাচ আকারের প্রয়োজন।
সলুব্রিস

@ সলুব্রিস, আপনি ঠিক বলেছেন! আমার খারাপ, এটি নির্দেশ করার জন্য ধন্যবাদ - আমি যদি রেফারেন্সের পদ্ধতিটি কীভাবে কাজ করে সে সম্পর্কে যদি একই ধারণা থাকে তবে আমি রেফারেন্সের জন্য মন্তব্যটি মুছব না।
অ্যালেক্স একারম্যান

গ্রাহককে প্রেরিত তালিকাটি এটিকে পরিবর্তনটি নিরাপদ করার জন্য অনুলিপি করা উচিত, যেমন: batchProcessor.accept (copyOf (ts))
Solubris

19

আমি এর মতো দৃশ্যের জন্য একটি কাস্টম স্প্লিটেটর লিখেছি। এটি ইনপুট স্ট্রিম থেকে প্রদত্ত আকারের তালিকা পূরণ করবে। এই পদ্ধতির সুবিধা হ'ল এটি অলস প্রক্রিয়াজাতকরণ করবে এবং এটি অন্যান্য স্ট্রিম ফাংশনগুলির সাথে কাজ করবে।

public static <T> Stream<List<T>> batches(Stream<T> stream, int batchSize) {
    return batchSize <= 0
        ? Stream.of(stream.collect(Collectors.toList()))
        : StreamSupport.stream(new BatchSpliterator<>(stream.spliterator(), batchSize), stream.isParallel());
}

private static class BatchSpliterator<E> implements Spliterator<List<E>> {

    private final Spliterator<E> base;
    private final int batchSize;

    public BatchSpliterator(Spliterator<E> base, int batchSize) {
        this.base = base;
        this.batchSize = batchSize;
    }

    @Override
    public boolean tryAdvance(Consumer<? super List<E>> action) {
        final List<E> batch = new ArrayList<>(batchSize);
        for (int i=0; i < batchSize && base.tryAdvance(batch::add); i++)
            ;
        if (batch.isEmpty())
            return false;
        action.accept(batch);
        return true;
    }

    @Override
    public Spliterator<List<E>> trySplit() {
        if (base.estimateSize() <= batchSize)
            return null;
        final Spliterator<E> splitBase = this.base.trySplit();
        return splitBase == null ? null
                : new BatchSpliterator<>(splitBase, batchSize);
    }

    @Override
    public long estimateSize() {
        final double baseSize = base.estimateSize();
        return baseSize == 0 ? 0
                : (long) Math.ceil(baseSize / (double) batchSize);
    }

    @Override
    public int characteristics() {
        return base.characteristics();
    }

}

সত্যিই সহায়ক। যদি কেউ কিছু কাস্টম মানদণ্ড (যেমন বাইটে সংগ্রহের আকারের) উপর ব্যাচ করতে চান তবে আপনি নিজের কাস্টম প্রিকেটটি পেশ করতে পারেন এবং এটি শর্ত হিসাবে লুপ-এ ব্যবহার করতে পারেন (তবে লুপ তখন আরও বেশি পঠনযোগ্য হবে)
pls

আমি বাস্তবায়ন সঠিক কিনা তা নিশ্চিত নই। উদাহরণস্বরূপ, যদি বেস স্ট্রিমটি SUBSIZEDফিরে আসে trySplitতবে বিভাজনের আগের তুলনায় আরও বেশি আইটেম থাকতে পারে (যদি স্প্যাচটি ব্যাচের মাঝখানে ঘটে)।
মাল্ট

@ মল্ট যদি আমার বোঝাপড়াটি Spliteratorsসঠিক হয়, তবে trySplitফলাফলটি কখনই মূলের চেয়ে বড় না হওয়া উচিত?
ব্রুস হ্যামিলটন

@ ব্রুসহ্যামিলটন দুর্ভাগ্যক্রমে, ডক্স অনুসারে অংশগুলি মোটামুটি সমান হতে পারে না । তাদের সমান হতে হবে :if this Spliterator is SUBSIZED, then estimateSize() for this spliterator before splitting must be equal to the sum of estimateSize() for this and the returned Spliterator after splitting.
মাল্ট

হ্যাঁ, এটি স্প্লিটেটর বিভাজন সম্পর্কে আমার বোঝার সাথে সামঞ্জস্যপূর্ণ। তবে, "ট্রায়সপ্লিট থেকে ফিরে আসা বিভাজনের বিভাজনের আগের চেয়ে আরও বেশি আইটেম কীভাবে থাকতে পারে" তা বুঝতে আমার খুব কষ্ট হচ্ছে, আপনি সেখানে কী বোঝাতে চেয়েছিলেন, সে সম্পর্কে কী আপনি বিস্তারিত বলতে পারেন?
ব্রুস হ্যামিল্টন

14

সমাধান করার জন্য আমাদের একই সমস্যা ছিল। আমরা সিস্টেম স্ট্রিমির চেয়ে বড় স্ট্রিমটি নিতে চেয়েছিলাম (একটি ডাটাবেসে সমস্ত বস্তুর মাধ্যমে পুনরাবৃত্তি করা) এবং যথাসম্ভব যথাক্রমে অর্ডারটিকে এলোমেলো করে দিতে - আমরা ভেবেছিলাম 10,000 টি আইটেম বাফার করা এবং এলোমেলো করে দেওয়া ঠিক হবে be

লক্ষ্যটি একটি ফাংশন যা একটি স্ট্রিম নিয়েছিল।

এখানে প্রস্তাবিত সমাধানগুলির মধ্যে অনেকগুলি বিকল্প রয়েছে বলে মনে হচ্ছে:

  • বিভিন্ন নন-জাভা 8 টি অতিরিক্ত গ্রন্থাগার ব্যবহার করুন
  • কোনও স্ট্রিম নয় এমন কোনও কিছু দিয়ে শুরু করুন - যেমন একটি এলোমেলো অ্যাক্সেসের তালিকা
  • একটি স্ট্রিম রয়েছে যা একটি স্প্লিট্রেটারে সহজেই বিভক্ত করা যায়

আমাদের প্রবৃত্তিটি মূলত একটি কাস্টম সংগ্রাহক ব্যবহার করার জন্য ছিল তবে এর অর্থ স্ট্রিমিং বাদ দেওয়া। উপরের কাস্টম সংগ্রাহকের সমাধানটি খুব ভাল এবং আমরা এটি প্রায় ব্যবহার করেছি।

এখানে একটি সমাধান যা সত্য যে ব্যবহার করে Cheats Streamগুলি আপনি একটি দিতে পারে Iteratorযা আপনি যেমন ব্যবহার করতে পারেন এড়িয়ে যাওয়ার জন্য একটি ডিম পাড়া আপনি কিছু অতিরিক্ত যে স্ট্রিম সমর্থন করি না করতে দেওয়া হয়। Iteratorএকটি স্ট্রিম জাভা 8 আরেকটি বিট ব্যবহার করে রূপান্তরিত ফিরে এসেছে StreamSupportজাদু।

/**
 * An iterator which returns batches of items taken from another iterator
 */
public class BatchingIterator<T> implements Iterator<List<T>> {
    /**
     * Given a stream, convert it to a stream of batches no greater than the
     * batchSize.
     * @param originalStream to convert
     * @param batchSize maximum size of a batch
     * @param <T> type of items in the stream
     * @return a stream of batches taken sequentially from the original stream
     */
    public static <T> Stream<List<T>> batchedStreamOf(Stream<T> originalStream, int batchSize) {
        return asStream(new BatchingIterator<>(originalStream.iterator(), batchSize));
    }

    private static <T> Stream<T> asStream(Iterator<T> iterator) {
        return StreamSupport.stream(
            Spliterators.spliteratorUnknownSize(iterator,ORDERED),
            false);
    }

    private int batchSize;
    private List<T> currentBatch;
    private Iterator<T> sourceIterator;

    public BatchingIterator(Iterator<T> sourceIterator, int batchSize) {
        this.batchSize = batchSize;
        this.sourceIterator = sourceIterator;
    }

    @Override
    public boolean hasNext() {
        prepareNextBatch();
        return currentBatch!=null && !currentBatch.isEmpty();
    }

    @Override
    public List<T> next() {
        return currentBatch;
    }

    private void prepareNextBatch() {
        currentBatch = new ArrayList<>(batchSize);
        while (sourceIterator.hasNext() && currentBatch.size() < batchSize) {
            currentBatch.add(sourceIterator.next());
        }
    }
}

এটির ব্যবহারের একটি সাধারণ উদাহরণটি দেখতে পাবেন:

@Test
public void getsBatches() {
    BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
        .forEach(System.out::println);
}

উপরের প্রিন্ট

[A, B, C]
[D, E, F]

আমাদের ব্যবহারের ক্ষেত্রে, আমরা ব্যাচগুলি এলোমেলো করে তারপরে স্ট্রিম হিসাবে রাখতে চেয়েছিলাম - দেখে মনে হয়েছিল:

@Test
public void howScramblingCouldBeDone() {
    BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
        // the lambda in the map expression sucks a bit because Collections.shuffle acts on the list, rather than returning a shuffled one
        .map(list -> {
            Collections.shuffle(list); return list; })
        .flatMap(List::stream)
        .forEach(System.out::println);
}

এটি এমন কিছু আউটপুট দেয় (এটি এলোমেলোভাবে তৈরি হয়, প্রতিবারের চেয়ে আলাদা)

A
C
B
E
D
F

এখানে সিক্রেট সস হ'ল সর্বদা একটি স্রোত থাকে, তাই আপনি হয় একটি ব্যাচের স্রোতে পরিচালনা করতে পারেন, বা প্রতিটি ব্যাচে কিছু করতে পারেন এবং তারপরে flatMapএটি আবার স্ট্রিমে ফিরে যেতে পারেন। আরও ভাল করে, উপরে শুধুমাত্র সব চূড়ান্ত হিসাবে সঞ্চালিত হয় forEachবা collectবা অন্যান্য সসীম এক্সপ্রেশন খিঁচ প্রবাহ মাধ্যমে ডেটা।

দেখা যাচ্ছে যে একটি স্ট্রিমের iteratorএকটি বিশেষ ধরণের সমাপ্তি অপারেশন এবং পুরো স্ট্রিমটি চলমান এবং স্মৃতিতে আসে না! একটি উজ্জ্বল ডিজাইনের জন্য জাভা 8 জনকে ধন্যবাদ!


এবং এটি খুব ভাল যে আপনি প্রতিটি ব্যাচটি সংগ্রহ করার পরে সম্পূর্ণরূপে পুনরাবৃত্তি করেন এবং একটি অব্যাহত রাখে আপনি Listঅভ্যন্তরীণ ব্যাচের উপাদানগুলির পুনরাবৃত্তি পিছিয়ে দিতে পারবেন না কারণ গ্রাহক পুরো ব্যাচটি এড়িয়ে যেতে চাইতে পারেন, এবং যদি আপনি এটি ব্যবহার না করেন তবে উপাদানগুলি তখন তারা খুব দূরে এড়িয়ে যাবেন না। (এগুলির একটি আমি সি # তে বাস্তবায়ন করেছি, যদিও এটি যথেষ্ট সহজ ছিল))
এরিক

9

আপনি আরএক্সজাভাও ব্যবহার করতে পারেন :

Observable.from(data).buffer(BATCH_SIZE).forEach((batch) -> process(batch));

বা

Observable.from(lazyFileStream).buffer(500).map((batch) -> process(batch)).toList();

বা

Observable.from(lazyFileStream).buffer(500).map(MyClass::process).toList();

8

আপনি সাইক্লোપ્સ-প্রতিক্রিয়াটি একবার দেখে নিতে পারেন , আমি এই গ্রন্থাগারের লেখক। এটি JOOλ ইন্টারফেসটি প্রয়োগ করে (এবং এক্সটেনশন জেডিকে 8 স্ট্রিমগুলি দ্বারা), তবে জেডিকে 8 সমান্তরাল স্ট্রিমগুলির বিপরীতে এটি অ্যাসিঙ্ক্রোনাস অপারেশনগুলিতে (যেমন সম্ভাব্য অ্যাসিঙ্ক আই / ও কলগুলি ব্লক করা) ফোকাস করে। জেডি কে সমান্তরাল স্ট্রিমস, সিপিইউ বাউন্ড অপারেশনের জন্য ডেটা প্যারালালিজমের বিপরীতে ফোকাস করে। এটি হুডের অধীনে ফিউচার ভিত্তিক কাজগুলির সমষ্টি পরিচালনা করে কাজ করে তবে শেষ ব্যবহারকারীদের কাছে একটি প্রমিত বর্ধিত স্ট্রিম এপিআই উপস্থাপন করে।

এই নমুনা কোডটি আপনাকে শুরু করতে সহায়তা করতে পারে

LazyFutureStream.parallelCommonBuilder()
                .react(data)
                .grouped(BATCH_SIZE)                  
                .map(this::process)
                .run();

এখানে ব্যাচিংয়ের একটি টিউটোরিয়াল রয়েছে

এবং আরও সাধারণ টিউটোরিয়াল এখানে

আপনার নিজের থ্রেড পুল (যা সম্ভবত I / O- কে ব্লক করার পক্ষে আরও উপযুক্ত) ব্যবহার করতে আপনি প্রক্রিয়া শুরু করতে পারেন

     LazyReact reactor = new LazyReact(40);

     reactor.react(data)
            .grouped(BATCH_SIZE)                  
            .map(this::process)
            .run();

3

খাঁটি জাভা 8 উদাহরণ যা সমান্তরাল স্ট্রিমগুলির সাথেও কাজ করে।

ব্যবহারবিধি:

Stream<Integer> integerStream = IntStream.range(0, 45).parallel().boxed();
CsStreamUtil.processInBatch(integerStream, 10, batch -> System.out.println("Batch: " + batch));

পদ্ধতি ঘোষণা এবং বাস্তবায়ন:

public static <ElementType> void processInBatch(Stream<ElementType> stream, int batchSize, Consumer<Collection<ElementType>> batchProcessor)
{
    List<ElementType> newBatch = new ArrayList<>(batchSize);

    stream.forEach(element -> {
        List<ElementType> fullBatch;

        synchronized (newBatch)
        {
            if (newBatch.size() < batchSize)
            {
                newBatch.add(element);
                return;
            }
            else
            {
                fullBatch = new ArrayList<>(newBatch);
                newBatch.clear();
                newBatch.add(element);
            }
        }

        batchProcessor.accept(fullBatch);
    });

    if (newBatch.size() > 0)
        batchProcessor.accept(new ArrayList<>(newBatch));
}


1

স্প্লিট্রেটার ব্যবহার করে সাধারণ উদাহরণ

    // read file into stream, try-with-resources
    try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
        //skip header
        Spliterator<String> split = stream.skip(1).spliterator();
        Chunker<String> chunker = new Chunker<String>();
        while(true) {              
            boolean more = split.tryAdvance(chunker::doSomething);
            if (!more) {
                break;
            }
        }           
    } catch (IOException e) {
        e.printStackTrace();
    }
}

static class Chunker<T> {
    int ct = 0;
    public void doSomething(T line) {
        System.out.println(ct++ + " " + line.toString());
        if (ct % 100 == 0) {
            System.out.println("====================chunk=====================");               
        }           
    }       
}

ব্রুসের উত্তরটি আরও বিস্তৃত, তবে আমি গুচ্ছ ফাইলগুলি প্রক্রিয়া করার জন্য দ্রুত এবং নোংরা কিছু খুঁজছিলাম।


1

এটি একটি খাঁটি জাভা সমাধান যা অলসভাবে মূল্যায়ন করা হয়।

public static <T> Stream<List<T>> partition(Stream<T> stream, int batchSize){
    List<List<T>> currentBatch = new ArrayList<List<T>>(); //just to make it mutable 
    currentBatch.add(new ArrayList<T>(batchSize));
    return Stream.concat(stream
      .sequential()                   
      .map(new Function<T, List<T>>(){
          public List<T> apply(T t){
              currentBatch.get(0).add(t);
              return currentBatch.get(0).size() == batchSize ? currentBatch.set(0,new ArrayList<>(batchSize)): null;
            }
      }), Stream.generate(()->currentBatch.get(0).isEmpty()?null:currentBatch.get(0))
                .limit(1)
    ).filter(Objects::nonNull);
}

1

আপনি apache.commons ব্যবহার করতে পারেন:

ListUtils.partition(ListOfLines, 500).stream()
                .map(partition -> processBatch(partition)
                .collect(Collectors.toList());

পার্টিশন অংশটি অলসভাবে করা হয় তবে তালিকাটি বিভাজনের পরে আপনি স্ট্রিমের সাথে কাজ করার সুবিধা পাবেন (যেমন সমান্তরাল স্ট্রিম ব্যবহার করুন, ফিল্টার যুক্ত করুন ইত্যাদি)। অন্যান্য উত্তরগুলি আরও বিস্তৃত সমাধানের পরামর্শ দেয় তবে কখনও কখনও পাঠযোগ্যতা এবং রক্ষণাবেক্ষণযোগ্যতা আরও গুরুত্বপূর্ণ (এবং কখনও কখনও সেগুলি হয় না :-))


কারা কমেছে তা নিশ্চিত নয় তবে কেন তা বুঝতে পেরে ভালো লাগবে .. আমি এমন একটি উত্তর দিয়েছি যা লোকেরা পেয়ারা ব্যবহার করতে না পারার জন্য অন্যান্য উত্তরগুলির পরিপূরক করেছে
তাল জোফে

আপনি এখানে একটি তালিকা প্রক্রিয়াকরণ করছেন, কোনও স্ট্রিম নয়।
ড্রেকমোর

@ ড্রাকমোর আমি উপ-তালিকার একটি ধারা প্রসেস করছি। স্ট্রিম () ফাংশন কলটি লক্ষ্য করুন
তাল জেফে

তবে প্রথমে আপনি এটিকে উপ-তালিকার তালিকায় পরিণত করেন, যা সত্য স্ট্রিমযুক্ত ডেটার জন্য সঠিকভাবে কাজ করবে না । পার্টিশনের রেফারেন্সটি এখানে: Commons.apache.org/proper/commons-collections/apidocs/org/…
ড্রেকমোর

4
টিবিএইচ আমি আপনার যুক্তি পুরোপুরি পাই না তবে আমি অনুমান করি আমরা একমত হতে সম্মত হতে পারি। আমি আমাদের কথোপকথন এখানে প্রতিফলিত করতে আমার উত্তর সম্পাদনা করেছি। আলোচনার জন্য ধন্যবাদ
তাল জোফফে

1

এটি সহজেই চুল্লি ব্যবহার করে করা যেতে পারে :

Flux.fromStream(fileReader.lines().onClose(() -> safeClose(fileReader)))
            .map(line -> someProcessingOfSingleLine(line))
            .buffer(BUFFER_SIZE)
            .subscribe(apiService::makeHttpRequest);

0

সহ Java 8এবং com.google.common.collect.Lists, আপনি এর মতো কিছু করতে পারেন:

public class BatchProcessingUtil {
    public static <T,U> List<U> process(List<T> data, int batchSize, Function<List<T>, List<U>> processFunction) {
        List<List<T>> batches = Lists.partition(data, batchSize);
        return batches.stream()
                .map(processFunction) // Send each batch to the process function
                .flatMap(Collection::stream) // flat results to gather them in 1 stream
                .collect(Collectors.toList());
    }
}

এখানে Tইনপুট তালিকার Uআইটেমগুলির ধরণ এবং আউটপুট তালিকার আইটেমগুলির ধরণ রয়েছে

এবং আপনি এটি এর মতো ব্যবহার করতে পারেন:

List<String> userKeys = [... list of user keys]
List<Users> users = BatchProcessingUtil.process(
    userKeys,
    10, // Batch Size
    partialKeys -> service.getUsers(partialKeys)
);
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.