আপনি কি একটি স্ট্রিমকে দুটি স্ট্রিমে বিভক্ত করতে পারবেন?


146

আমার কাছে একটি ডেটা সেট আছে যা জাভা 8 স্ট্রিমের প্রতিনিধিত্ব করে:

Stream<T> stream = ...;

আমি এলোমেলো উপসেট পেতে এটি কীভাবে ফিল্টার করব তা দেখতে পাচ্ছি - উদাহরণস্বরূপ

Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();   
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));

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

(heads, tails) = stream.[some kind of split based on filter]

কোন অন্তর্দৃষ্টি জন্য ধন্যবাদ।


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

উত্তর:


9

বেপারটা এমন না. আপনি Streamএকটির মধ্যে দুটি গুলি পেতে পারেন না ; এটি অর্থবোধ করে না - আপনি একই সাথে অন্যটি উত্পন্ন করার প্রয়োজন ছাড়াই একটির উপর কীভাবে পুনরাবৃত্তি করবেন? একটি স্ট্রিম কেবল একবারে পরিচালিত হতে পারে।

তবে আপনি যদি এগুলিকে একটি তালিকা বা অন্য কিছুতে ফেলে দিতে চান তবে আপনি করতে পারেন

stream.forEach((x) -> ((x == 0) ? heads : tails).add(x));

65
কেন তা বোধ হয় না? যেহেতু স্ট্রিমটি পাইপলাইন হওয়ার কোনও কারণ নেই কারণ এটি মূল স্ট্রিমের দুটি প্রযোজক তৈরি করতে পারে না, তাই আমি দেখতে পেলাম যে এটি দুটি সংগ্রাহক সরবরাহ করে যা দুটি স্ট্রিম সরবরাহ করে।
ব্রেট রায়ান

36
থ্রেড নিরাপদ নয়। খারাপ পরামর্শ সরাসরি কোনও সংগ্রহে যোগ করার চেষ্টা করছে, এজন্য আমাদের stream.collect(...)পূর্বনির্ধারিত থ্রেড-নিরাপদ রয়েছে Collectors, এটি নন-থ্রেড-নিরাপদ সংগ্রহগুলিতেও (কোনও সিঙ্ক্রোনাইজড লক কনটেন্ট নেই) ভালভাবে কাজ করে। @ মার্ক জারনিমাসের সেরা উত্তর
YoYo

1
@ জোড মাথা এবং লেজগুলি থ্রেড-নিরাপদ থাকলে এটি থ্রেড-নিরাপদ। অতিরিক্তভাবে, অ-সমান্তরাল স্ট্রিমের ব্যবহার ধরে রেখে, কেবলমাত্র আদেশের গ্যারান্টি নেই, তাই তারা থ্রেড-নিরাপদ। সম্মতি সংক্রান্ত সমস্যাগুলি ঠিক করার জন্য এটি প্রোগ্রামারটির উপর নির্ভর করে, যদি সংগ্রহগুলি থ্রেড নিরাপদ থাকে তবে এই উত্তরটি পুরোপুরি উপযুক্ত।
নিকোলাস

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

1
@ জোড এটির চেয়ে ভাল সমাধান নয়, এটি আসলেই আরও অদক্ষ। চিন্তাভাবনার অবশেষে এই সিদ্ধান্তে শেষ হয় যে অনাকাঙ্ক্ষিত পরিণতি রোধ করতে সমস্ত সংগ্রহগুলি ডিফল্টরূপে সুরক্ষিত হওয়া উচিত, যা কেবল ভুল simply
নিকোলাস

301

এই জন্য একটি সংগ্রাহক ব্যবহার করা যেতে পারে।

  • দুটি বিভাগের জন্য, Collectors.partitioningBy()কারখানাটি ব্যবহার করুন ।

এই তৈরি করবে Mapথেকে Booleanথেকে List, এবং অথবা এক আইটেম একটি ভিত্তিক অন্য তালিকা করা Predicate

দ্রষ্টব্য: যেহেতু স্ট্রিমটি পুরোভাবে গ্রাস করা দরকার তাই এটি অসীম স্ট্রিমগুলিতে কাজ করতে পারে না। এবং যেহেতু স্ট্রিমটি গ্রাস করা হয়েছে, এই পদ্ধতিটি এগুলিকে মেমরির সাথে একটি নতুন স্ট্রিম তৈরি করার পরিবর্তে তালিকাগুলিতে রাখে। যদি আপনার আউটপুট হিসাবে স্ট্রিমের প্রয়োজন হয় তবে আপনি সর্বদা সেই তালিকাটি স্ট্রিম করতে পারেন।

এছাড়াও, পুনরাবৃত্তির প্রয়োজন নেই, এমনকি আপনি সরবরাহ করেছেন এমন শীর্ষস্থানীয় উদাহরণগুলিতেও নয়।

  • বাইনারি বিভাজন দেখতে এরকম দেখাচ্ছে:
Random r = new Random();

Map<Boolean, List<String>> groups = stream
    .collect(Collectors.partitioningBy(x -> r.nextBoolean()));

System.out.println(groups.get(false).size());
System.out.println(groups.get(true).size());
  • আরও বিভাগগুলির জন্য, একটি Collectors.groupingBy()কারখানা ব্যবহার করুন ।
Map<Object, List<String>> groups = stream
    .collect(Collectors.groupingBy(x -> r.nextInt(3)));
System.out.println(groups.get(0).size());
System.out.println(groups.get(1).size());
System.out.println(groups.get(2).size());

যদি স্ট্রিমগুলি না হয় Streamতবে আদিম স্ট্রিমগুলির মধ্যে একটি পছন্দ করে IntStream, তবে এই .collect(Collectors)পদ্ধতিটি উপলভ্য নয়। সংগ্রাহকের কারখানা ছাড়া আপনাকে এটি ম্যানুয়াল পদ্ধতিতে করতে হবে। এটি বাস্তবায়নের মতো দেখাচ্ছে:

[২০২০-০৪-১-16 সালের পরে 2.0 উদাহরণ]

    IntStream    intStream = IntStream.iterate(0, i -> i + 1).limit(100000).parallel();
    IntPredicate predicate = ignored -> r.nextBoolean();

    Map<Boolean, List<Integer>> groups = intStream.collect(
            () -> Map.of(false, new ArrayList<>(100000),
                         true , new ArrayList<>(100000)),
            (map, value) -> map.get(predicate.test(value)).add(value),
            (map1, map2) -> {
                map1.get(false).addAll(map2.get(false));
                map1.get(true ).addAll(map2.get(true ));
            });

এই উদাহরণে আমি প্রাথমিক সংগ্রহের পুরো আকারের সাথে অ্যারেলিস্টগুলি সূচনা করি (এটি যদি একেবারেই জানা থাকে)। এটি সবচেয়ে খারাপ পরিস্থিতিতে এমনকি আকার পরিবর্তন করতে বাধা দেয় তবে সম্ভাব্য 2 * N * টি স্পেস (এন = প্রাথমিক উপাদানগুলির সংখ্যা, টি = থ্রেডের সংখ্যা) পেতে পারে। গতির জন্য স্থান বাণিজ্য করার জন্য, আপনি এটিকে ছেড়ে দিতে পারেন বা আপনার সেরা শিক্ষিত অনুমান ব্যবহার করতে পারেন, যেমন একটি পার্টিশনে প্রত্যাশিত সর্বোচ্চ সংখ্যক উপাদান (সাধারণত সুষম বিভাজনের জন্য N / 2 এর উপরে)।

আমি আশা করি জাভা 9 পদ্ধতি ব্যবহার করে আমি কাউকে আপত্তি করি না। জাভা 8 সংস্করণের জন্য, সম্পাদনা ইতিহাসটি দেখুন।


2
সুন্দর। তবে, সমান্তরাল প্রবাহের ক্ষেত্রে ইনটস্ট্রিমের শেষ সমাধান থ্রেড-নিরাপদ হবে না। সমাধানটি আপনি যতটা সহজ ভাবেন তত সহজ ... stream.boxed().collect(...);! এটি বিজ্ঞাপন হিসাবে করা হবে: IntStreamআদিমকে বক্স Stream<Integer>সংস্করণে রূপান্তর করুন ।
YoYo

32
এটি গ্রহণযোগ্য উত্তর হওয়া উচিত কারণ এটি সরাসরি ওপি প্রশ্নের সমাধান করে।
এজেল

27
আমি আশা করি স্ট্যাক ওভারফ্লো আরও ভাল কোনও সন্ধান পেলে সম্প্রদায়টিকে নির্বাচিত উত্তরগুলিকে ওভাররাইড করার অনুমতি দেয়।
গুইসিম

আমি নিশ্চিত না যে এই প্রশ্নের উত্তর দেয়। প্রশ্নটি একটি স্ট্রিমকে স্ট্রিমে বিভক্ত করার অনুরোধ করে - তালিকাগুলি নয়।
অ্যালিকেলজিন-কিলাকা

1
আহরণকারী ফাংশন অযথা ভার্চুজের। পরিবর্তে (map, x) -> { boolean partition = p.test(x); List<Integer> list = map.get(partition); list.add(x); }আপনি সহজভাবে ব্যবহার করতে পারেন (map, x) -> map.get(p.test(x)).add(x)। আরও, collectঅপারেশনটি থ্রেড-সেফ না হওয়ার কোনও কারণ আমি দেখতে পাচ্ছি না। এটি ঠিক যেমন কাজ করে বলে কাজ করা হয় এবং কীভাবে Collectors.partitioningBy(p)এটি কাজ করবে তার খুব কাছাকাছি কাজ করে। তবে আমি দুবার বক্সিং এড়ানোর জন্য ব্যবহার না করার IntPredicateপরিবর্তে একটি Predicate<Integer>ব্যবহার করব boxed()
হলগার

21

আমি এই প্রশ্নটি আমার নিজের কাছে গিয়েছিলাম এবং আমি অনুভব করি যে একটি কাঁটাযুক্ত স্ট্রিমের এমন কিছু ব্যবহার রয়েছে যা বৈধ প্রমাণ করতে পারে। আমি ভোক্তা হিসাবে নীচের কোডটি লিখেছিলাম যাতে এটি কিছু না করে তবে আপনি এটি ফাংশনগুলিতে এবং আপনি যে কোনও কিছুতে আসতে পারেন এটি প্রয়োগ করতে পারেন।

class PredicateSplitterConsumer<T> implements Consumer<T>
{
  private Predicate<T> predicate;
  private Consumer<T>  positiveConsumer;
  private Consumer<T>  negativeConsumer;

  public PredicateSplitterConsumer(Predicate<T> predicate, Consumer<T> positive, Consumer<T> negative)
  {
    this.predicate = predicate;
    this.positiveConsumer = positive;
    this.negativeConsumer = negative;
  }

  @Override
  public void accept(T t)
  {
    if (predicate.test(t))
    {
      positiveConsumer.accept(t);
    }
    else
    {
      negativeConsumer.accept(t);
    }
  }
}

এখন আপনার কোড বাস্তবায়ন এরকম কিছু হতে পারে:

personsArray.forEach(
        new PredicateSplitterConsumer<>(
            person -> person.getDateOfBirth().isPresent(),
            person -> System.out.println(person.getName()),
            person -> System.out.println(person.getName() + " does not have Date of birth")));

20

দুর্ভাগ্যক্রমে, আপনি যা জিজ্ঞাসা করেছেন তা সরাসরি জাভাডক স্ট্রিমে নকল করা হয়েছে :

একটি স্ট্রিম কেবল একবারই চালানো উচিত (মধ্যবর্তী বা টার্মিনাল স্ট্রিম অপারেশনকে অনুরোধ করা)। এটি বাতিল করে দেয়, উদাহরণস্বরূপ, "কাঁটাচামচ" স্ট্রিমগুলি যেখানে একই উত্স দুটি বা ততোধিক পাইপলাইন, বা একই স্ট্রিমের একাধিক ট্র্যাভারসাল ফিড করে।

আপনি peekসত্যিকারের আচরণের এই ধরণের ইচ্ছা করা উচিত বা অন্যান্য পদ্ধতি ব্যবহার করে আপনি এটিকে ঘিরে কাজ করতে পারেন । এই ক্ষেত্রে, আপনাকে যা করা উচিত তা হ'ল ফোরকিং ফিল্টার সহ একই মূল স্ট্রিম উত্স থেকে দুটি স্ট্রিম ব্যাক করার চেষ্টা করার পরিবর্তে, আপনি নিজের স্ট্রীমটিকে নকল করবেন এবং প্রতিলিপিগুলির যথাযথভাবে ফিল্টার করবেন।

তবে Streamআপনার ব্যবহারের ক্ষেত্রে যদি উপযুক্ত কাঠামো থাকে তবে আপনি পুনর্বিবেচনা করতে পারেন।


6
Javadoc বাক্যে কথন বিভিন্ন স্ট্রিম মধ্যে পার্টিশন অগ্রাহ্য করে না যতদিন একটি একক প্রবাহ আইটেমটি শুধুমাত্র যায় এক এগুলোর
Thorbjørn Ravn অ্যান্ডারসনকে

2
@ থরবজর্নআরভান অ্যান্ডারসেন আমি নিশ্চিত নই যে কোনও স্ট্রিম আইটেমটির নকল করা একটি জালযুক্ত প্রবাহের প্রধান প্রতিবন্ধক। মূল সমস্যাটি হ'ল ফোরিং অপারেশনটি মূলত একটি টার্মিনাল অপারেশন, সুতরাং আপনি কাঁটাচামচ করার সিদ্ধান্ত নেওয়ার সময় আপনি মূলত কোনও ধরণের সংগ্রহ তৈরি করছেন। উদাহরণস্বরূপ আমি কোনও পদ্ধতি লিখতে পারি List<Stream> forkStream(Stream s)তবে আমার ফলে প্রাপ্ত স্ট্রিমগুলি কমপক্ষে আংশিকভাবে সংগ্রহের দ্বারা সমর্থিত হবে এবং সরাসরি অন্তর্নিহিত স্ট্রিম দ্বারা নয়, filterযা কোনও টার্মিনাল স্ট্রিম অপারেশন নয় তা বলার বিপরীতে ।
ট্রেভর ফ্রিম্যান

7
আমি গিথুব.com/ রিচার্টিএক্স / আরএক্স জাভা / উইকির তুলনায় জাভা স্ট্রিমগুলি কিছুটা অর্ধ- নির্ধারিত মনে করার কারণগুলির একটি কারণ কারণ স্ট্রিমের বিন্দুটি একটি সম্ভাব্য অসীম উপাদানগুলির ক্রিয়াকলাপ প্রয়োগ করা এবং বাস্তব বিশ্বের ক্রিয়াকলাপগুলি প্রায়শই বিভাজনের প্রয়োজন হয় , সদৃশ এবং মার্জিং স্ট্রীমগুলি।
উসমান ইসমাইল

8

এটি স্ট্রিমের সাধারণ ব্যবস্থার বিরুদ্ধে। বলুন যে আপনি স্ট্রিম এস 0 কে সা ও এসবিতে ভাগ করে নিতে পারেন নিজের পছন্দ মতো। যে কোনও টার্মিনাল ক্রিয়াকলাপ সম্পাদন করে বলুন count(), সা এ অগত্যা এস 0 এর সমস্ত উপাদান "গ্রাস" করবে। অতএব এসবি এর ডেটা উত্স হারিয়েছে।

পূর্বে, স্ট্রিমের একটি tee()পদ্ধতি ছিল বলে আমি মনে করি, যা একটি স্ট্রিমটিকে দুটি করে নকল করে। এটি এখন সরানো হয়েছে।

স্ট্রিমের একটি উঁকি দেওয়া () পদ্ধতি রয়েছে যদিও, আপনি আপনার প্রয়োজনীয়তা অর্জনের জন্য এটি ব্যবহার করতে সক্ষম হতে পারেন।


1
peekঠিক কি ব্যবহার করা হয় হয় tee
লুই ওয়াসারম্যান

5

ঠিক নয়, তবে আপনি যা চান তা পূরণ করতে সক্ষম হতে পারেন Collectors.groupingBy()। আপনি একটি নতুন সংগ্রহ তৈরি করেন এবং তারপরে সেই নতুন সংগ্রহটিতে স্ট্রিম ইনস্ট্যান্ট করতে পারেন।


2

এটি আমি নিয়ে আসতে পারি সবচেয়ে কম খারাপ উত্তর ছিল।

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

public class Test {

    public static <T, L, R> Pair<L, R> splitStream(Stream<T> inputStream, Predicate<T> predicate,
            Function<Stream<T>, L> trueStreamProcessor, Function<Stream<T>, R> falseStreamProcessor) {

        Map<Boolean, List<T>> partitioned = inputStream.collect(Collectors.partitioningBy(predicate));
        L trueResult = trueStreamProcessor.apply(partitioned.get(Boolean.TRUE).stream());
        R falseResult = falseStreamProcessor.apply(partitioned.get(Boolean.FALSE).stream());

        return new ImmutablePair<L, R>(trueResult, falseResult);
    }

    public static void main(String[] args) {

        Stream<Integer> stream = Stream.iterate(0, n -> n + 1).limit(10);

        Pair<List<Integer>, String> results = splitStream(stream,
                n -> n > 5,
                s -> s.filter(n -> n % 2 == 0).collect(Collectors.toList()),
                s -> s.map(n -> n.toString()).collect(Collectors.joining("|")));

        System.out.println(results);
    }

}

এটি পূর্ণসংখ্যার একটি স্ট্রিম নেয় এবং এগুলিকে 5 এ বিভক্ত করে তোলে 5 এর বেশি যারা তাদের জন্য এটি কেবল এমনকি সংখ্যার ফিল্টার করে এবং তাদের একটি তালিকায় রাখে। বাকি জন্য এটি তাদের সাথে যোগ দেয় |

আউটপুট:

 ([6, 8],0|1|2|3|4|5)

এটি আদর্শ নয় কারণ এটি স্ট্রিম ভেঙে মধ্যস্থতাকারী সংগ্রহগুলিতে সমস্ত কিছু সংগ্রহ করে (এবং এর মধ্যে অনেকগুলি যুক্তি রয়েছে!)


1

কোনও প্রবাহের বাইরে থেকে নির্দিষ্ট উপাদানগুলিকে ফিল্টার করার জন্য এবং ত্রুটি হিসাবে তাদের লগ ইন করার কোনও উপায় খুঁজতে গিয়ে আমি এই প্রশ্নটিতে হোঁচট খেয়েছি। সুতরাং আমার সত্যিকারের স্ট্রিমটিকে এতটা বিভক্ত করার দরকার নেই যেহেতু নিরবচ্ছিন্ন সিনট্যাক্সের সাথে একটি প্রাকটিকের সাথে একটি অকাল সমাপনী ক্রিয়া সংযুক্ত করা উচিত। এটিই আমি নিয়ে এসেছি:

public class MyProcess {
    /* Return a Predicate that performs a bail-out action on non-matching items. */
    private static <T> Predicate<T> withAltAction(Predicate<T> pred, Consumer<T> altAction) {
    return x -> {
        if (pred.test(x)) {
            return true;
        }
        altAction.accept(x);
        return false;
    };

    /* Example usage in non-trivial pipeline */
    public void processItems(Stream<Item> stream) {
        stream.filter(Objects::nonNull)
              .peek(this::logItem)
              .map(Item::getSubItems)
              .filter(withAltAction(SubItem::isValid,
                                    i -> logError(i, "Invalid")))
              .peek(this::logSubItem)
              .filter(withAltAction(i -> i.size() > 10,
                                    i -> logError(i, "Too large")))
              .map(SubItem::toDisplayItem)
              .forEach(this::display);
    }
}

0

লম্বোক ব্যবহার করে সংক্ষিপ্ত সংস্করণ

import java.util.function.Consumer;
import java.util.function.Predicate;

import lombok.RequiredArgsConstructor;

/**
 * Forks a Stream using a Predicate into postive and negative outcomes.
 */
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PROTECTED)
public class StreamForkerUtil<T> implements Consumer<T> {
    Predicate<T> predicate;
    Consumer<T> positiveConsumer;
    Consumer<T> negativeConsumer;

    @Override
    public void accept(T t) {
        (predicate.test(t) ? positiveConsumer : negativeConsumer).accept(t);
    }
}

-3

কেমন:

Supplier<Stream<Integer>> randomIntsStreamSupplier =
    () -> (new Random()).ints(0, 2).boxed();

Stream<Integer> tails =
    randomIntsStreamSupplier.get().filter(x->x.equals(0));
Stream<Integer> heads =
    randomIntsStreamSupplier.get().filter(x->x.equals(1));

1
যেহেতু সরবরাহকারীকে দু'বার কল করা হয়, আপনি দুটি ভিন্ন এলোমেলো সংগ্রহ পাবেন। আমি মনে করি যে একই উত্পন্ন ক্রমের মধ্যে
সন্ধ্যা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.