IT-Swarm.Net

एक जावा 8 स्ट्रीम की स्थापना

जावा 8 स्ट्रीम पर "विभाजन" ऑपरेशन को कैसे लागू किया जाए? विभाजन से मेरा मतलब है, एक धारा को किसी दिए गए आकार की उप धाराओं में विभाजित करना। किसी तरह यह अमरूद Iterators.partition () विधि के समान होगा, बस यह वांछनीय है कि विभाजन सूची के बजाय आलसी मूल्यांकन किए गए स्ट्रीम हैं।.

54
Trader001

निश्चित आकार के बैचों में मनमाने स्रोत धारा को विभाजित करना असंभव है, क्योंकि इससे समानांतर प्रसंस्करण खराब हो जाएगा। समानांतर में प्रसंस्करण करते समय आप नहीं जान सकते हैं कि विभाजन के बाद पहले उप-कार्य में कितने तत्व हैं, इसलिए आप अगले उप-कार्य के लिए विभाजन तब तक नहीं बना सकते जब तक कि पहले पूरी तरह से संसाधित नहीं हो जाते।.

हालाँकि यादृच्छिक पहुँच Listसे विभाजन की धारा बनाना संभव है। ऐसी सुविधा उपलब्ध है, उदाहरण के लिए, मेरी StreamExNAME _ पुस्तकालय में:

List<Type> input = Arrays.asList(...);

Stream<List<Type>> stream = StreamEx.ofSubLists(input, partitionSize);

या यदि आप वास्तव में धाराओं की धारा चाहते हैं:

Stream<Stream<Type>> stream = StreamEx.ofSubLists(input, partitionSize).map(List::stream);

यदि आप तृतीय-पक्ष लाइब्रेरी पर निर्भर नहीं होना चाहते हैं, तो आप ऐसे ofSubListsविधि को मैन्युअल रूप से लागू कर सकते हैं:

public static <T> Stream<List<T>> ofSubLists(List<T> source, int length) {
    if (length <= 0)
        throw new IllegalArgumentException("length = " + length);
    int size = source.size();
    if (size <= 0)
        return Stream.empty();
    int fullChunks = (size - 1) / length;
    return IntStream.range(0, fullChunks + 1).mapToObj(
        n -> source.subList(n * length, n == fullChunks ? size : (n + 1) * length));
}

यह कार्यान्वयन थोड़ा लंबा दिखता है, लेकिन यह कुछ कोने के मामलों को ध्यान में रखता है जैसे कि करीब-करीब MAX_VALUE सूची आकार।.


यदि आप अनियंत्रित स्ट्रीम के लिए समानांतर-अनुकूल समाधान चाहते हैं (तो आपको परवाह नहीं है कि एकल बैच में कौन से स्ट्रीम तत्व संयुक्त होंगे), आप इस तरह कलेक्टर का उपयोग कर सकते हैं (प्रेरणा के लिए @sibnick के लिए धन्यवाद):

public static <T, A, R> Collector<T, ?, R> unorderedBatches(int batchSize, 
                   Collector<List<T>, A, R> downstream) {
    class Acc {
        List<T> cur = new ArrayList<>();
        A acc = downstream.supplier().get();
    }
    BiConsumer<Acc, T> accumulator = (acc, t) -> {
        acc.cur.add(t);
        if(acc.cur.size() == batchSize) {
            downstream.accumulator().accept(acc.acc, acc.cur);
            acc.cur = new ArrayList<>();
        }
    };
    return Collector.of(Acc::new, accumulator,
            (acc1, acc2) -> {
                acc1.acc = downstream.combiner().apply(acc1.acc, acc2.acc);
                for(T t : acc2.cur) accumulator.accept(acc1, t);
                return acc1;
            }, acc -> {
                if(!acc.cur.isEmpty())
                    downstream.accumulator().accept(acc.acc, acc.cur);
                return downstream.finisher().apply(acc.acc);
            }, Collector.Characteristics.UNORDERED);
}

उपयोग उदाहरण:

List<List<Integer>> list = IntStream.range(0,20)
                                    .boxed().parallel()
                                    .collect(unorderedBatches(3, Collectors.toList()));

परिणाम:

[[2, 3, 4], [7, 8, 9], [0, 1, 5], [12, 13, 14], [17, 18, 19], [10, 11, 15], [6, 16]]

इस तरह के कलेक्टर पूरी तरह से थ्रेड-सेफ हैं और क्रमिक प्रवाह के लिए ऑर्डर किए गए बैचों का उत्पादन करते हैं।.

यदि आप प्रत्येक बैच के लिए एक मध्यवर्ती परिवर्तन लागू करना चाहते हैं, तो आप निम्न संस्करण का उपयोग कर सकते हैं:

public static <T, AA, A, B, R> Collector<T, ?, R> unorderedBatches(int batchSize,
        Collector<T, AA, B> batchCollector,
        Collector<B, A, R> downstream) {
    return unorderedBatches(batchSize, 
            Collectors.mapping(list -> list.stream().collect(batchCollector), downstream));
}

उदाहरण के लिए, इस तरह आप हर बैच में संख्याओं को उड़ने पर जोड़ सकते हैं:

List<Integer> list = IntStream.range(0,20)
        .boxed().parallel()
        .collect(unorderedBatches(3, Collectors.summingInt(Integer::intValue), 
            Collectors.toList()));
38
Tagir Valeev

बशर्ते आप क्रमिक रूप से स्ट्रीम का उपयोग करना चाहते हैं, स्ट्रीम को विभाजन करना संभव है (साथ ही संबंधित कार्य जैसे कि विंडोिंग - जो मुझे लगता है कि आप वास्तव में इस मामले में चाहते हैं)। दो पुस्तकालय जो मानक धाराओं के लिए विभाजन का समर्थन करेंगे साइक्लोप्स-प्रतिक्रिया (मैं लेखक हूँ) और jOOλ जो साइक्लॉप्स-रिएक्शन फैली हुई है (विंडोडिंग जैसी कार्यक्षमता जोड़ने के लिए)।.

साइक्लोप्स-स्ट्रीम में जावा स्ट्रीम्स के संचालन के लिए स्थैतिक कार्यों स्ट्रीमयूटिल्स का एक संग्रह है, और विभाजन, हेडआंडटेल, स्प्लिटबाई, विभाजन के लिए विभाजन जैसे कार्यों की श्रृंखला है ।.

आकार 30 के नेस्टेड स्ट्रीम की स्ट्रीम में स्ट्रीम करने के लिए आप विंडो विधि का उपयोग कर सकते हैं।.

OPs बिंदु तक, स्ट्रीमिंग शब्दों में, किसी दिए गए आकार की कई धाराओं में स्ट्रीम विभाजन एक घुमावदार ऑपरेशन है (एक विभाजन ऑपरेशन के बजाय)।.

  Stream<Streamable<Integer>> streamOfStreams = StreamUtils.window(stream,30);

एक स्ट्रीम एक्सटेंशन क्लास कहा जाता है रिएक्टिवसेक जो फैली हुई है जूल.सेक विंडिंग कार्यक्षमता को जोड़ता है, जिससे कोड थोड़ा साफ हो सकता है।.

  ReactiveSeq<Integer> seq;
  ReactiveSeq<ListX<Integer>> streamOfLists = seq.grouped(30);

हालांकि टैगिर ऊपर बताते हैं, यह समानांतर धाराओं के लिए उपयुक्त नहीं है। यदि आप किसी स्ट्रीम को विंडो या बैच करना चाहते हैं जिसे आप मल्टीथ्रेडेड फैशन में निष्पादित करना चाहते हैं। LazyFutureStream में साइक्लोप्स-प्रतिक्रिया उपयोगी हो सकती है (विंड-टू-डू सूची में है, लेकिन सादे पुराने बैचिंग अब उपलब्ध है)।.

इस मामले में डेटा को बहु-निर्माता/एकल-उपभोक्ता प्रतीक्षा-मुक्त कतार में स्ट्रीम को निष्पादित करने वाले कई थ्रेड्स से पारित किया जाएगा और उस कतार से अनुक्रमिक डेटा को फिर से थ्रेड्स में वितरित किए जाने से पहले विंडो किया जा सकता है।.

  Stream<List<Data>> batched = new LazyReact().range(0,1000)
                                              .grouped(30)
                                              .map(this::process);
8
John McClean

ऐसा लगता है, जैसा कि जॉन स्कीट ने अपनी टिप्पणी में दिखाया है, यह विभाजन को आलसी बनाना संभव नहीं है। गैर-आलसी विभाजन के लिए, मेरे पास पहले से ही यह कोड है:

public static <T> Stream<Stream<T>> partition(Stream<T> source, int size) {
    final Iterator<T> it = source.iterator();
    final Iterator<Stream<T>> partIt = Iterators.transform(Iterators.partition(it, size), List::stream);
    final Iterable<Stream<T>> iterable = () -> partIt;

    return StreamSupport.stream(iterable.spliterator(), false);
}
6
Trader001

इस समस्या के लिए सबसे सुंदर और शुद्ध जावा 8 समाधान मैंने पाया:

public static <T> List<List<T>> partition(final List<T> list, int batchSize) {
return IntStream.range(0, getNumberOfPartitions(list, batchSize))
                .mapToObj(i -> list.subList(i * batchSize, Math.min((i + 1) * batchSize, list.size())))
                .collect(toList());
}

//https://stackoverflow.com/questions/23246983/get-the-next-higher-integer-value-in-Java
private static <T> int getNumberOfPartitions(List<T> list, int batchSize) {
    return (list.size() + batchSize- 1) / batchSize;
}
2
rloeffel

मुझे एक सुंदर समाधान मिला: Iterable parts = Iterables::partition(stream::iterator, size)

2
WarGoth

यह एक शुद्ध जावा समाधान है जिसे सूची का उपयोग करने के बजाय आलसी का मूल्यांकन किया जाता है।.

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);
}

लचीलापन के लिए विधि Stream<List<T>> लौटाती है। आप इसे Stream<Stream<T>> से आसानी से partition(something, 10).map(List::stream) में बदल सकते हैं।.

1
Hei

मुझे लगता है कि यह किसी प्रकार की हैक के साथ संभव है:

बैच के लिए उपयोगिता वर्ग बनाएं:

public static class ConcurrentBatch {
    private AtomicLong id = new AtomicLong();
    private int batchSize;

    public ConcurrentBatch(int batchSize) {
        this.batchSize = batchSize;
    }

    public long next() {
        return (id.getAndIncrement()) / batchSize;
    }

    public int getBatchSize() {
        return batchSize;
    }
}

और तरीके:

public static <T> void applyConcurrentBatchToStream(Consumer<List<T>> batchFunc, Stream<T> stream, int batchSize){
    ConcurrentBatch batch = new ConcurrentBatch(batchSize);
    //hack Java map: extends and override computeIfAbsent
    Supplier<ConcurrentMap<Long, List<T>>> mapFactory = () -> new ConcurrentHashMap<Long, List<T>>() {
        @Override
        public List<T> computeIfAbsent(Long key, Function<? super Long, ? extends List<T>> mappingFunction) {
            List<T> rs = super.computeIfAbsent(key, mappingFunction);
            //apply batchFunc to old lists, when new batch list is created
            if(rs.isEmpty()){
                for(Entry<Long, List<T>> e : entrySet()) {
                    List<T> batchList = e.getValue();
                    //todo: need to improve
                    synchronized (batchList) {
                        if (batchList.size() == batch.getBatchSize()){
                            batchFunc.accept(batchList);
                            remove(e.getKey());
                            batchList.clear();
                        }
                    }
                }
            }
            return rs;
        }
    };
    stream.map(s -> new AbstractMap.SimpleEntry<>(batch.next(), s))
            .collect(groupingByConcurrent(AbstractMap.SimpleEntry::getKey, mapFactory, mapping(AbstractMap.SimpleEntry::getValue, toList())))
            .entrySet()
            .stream()
            //map contains only unprocessed lists (size<batchSize)
            .forEach(e -> batchFunc.accept(e.getValue()));
}
0
sibnick

यहाँ त्वरित समाधान द्वारा अबाकसुतिल

IntStream.range(0, Integer.MAX_VALUE).split(size).forEach(s -> N.println(s.toArray()));

अस्वीकरण: मैं AbacusUtil का डेवलपर हूं।.

0
user_3380739