একটি জাভা 8 স্ট্রিম ফাঁকা আছে কিনা তা কীভাবে পরীক্ষা করবেন?


100

আমি কীভাবে চেক করতে পারি Streamনন-টার্মিনাল অপারেশন হিসাবে খালি ব্যতিক্রম নষ্ট ?

মূলত, আমি নীচের কোডের সমতুল্য কিছু খুঁজছি, কিন্তু এর মধ্যে স্ট্রিমটি বাস্তবায়ন না করেই। বিশেষত, টার্মিনাল ক্রিয়াকলাপটি স্ট্রিমটি আসলে গ্রাস করার আগে চেকটি হওয়া উচিত নয়।

public Stream<Thing> getFilteredThings() {
    Stream<Thing> stream = getThings().stream()
                .filter(Thing::isFoo)
                .filter(Thing::isBar);
    return nonEmptyStream(stream, () -> {
        throw new RuntimeException("No foo bar things available")   
    });
}

private static <T> Stream<T> nonEmptyStream(Stream<T> stream, Supplier<T> defaultValue) {
    List<T> list = stream.collect(Collectors.toList());
    if (list.isEmpty()) list.add(defaultValue.get());
    return list.stream();
}

23
আপনার কেক নেই এবং এটিও খেতে পারবেন না - এবং বেশ আক্ষরিকভাবে তাই, এই প্রসঙ্গে। যা শুরু করেছো গ্রাস প্রবাহ খুঁজে বের করতে যদি এটা খালি। এটি স্ট্রিমের শব্দার্থবিজ্ঞানের মূল বিষয় (অলসতা)।
মার্কো টপলনিক

এটি শেষ পর্যন্ত গ্রাস করা হবে, এই মুহুর্তে চেকটি হওয়া উচিত
সেফালপড

12
স্ট্রিমটি খালি নয় তা পরীক্ষা করার জন্য আপনাকে কমপক্ষে একটি উপাদান গ্রহণ করার চেষ্টা করতে হবে। এই মুহূর্তে স্ট্রিমটি তার "কুমারীত্ব" হারিয়েছে এবং শুরু থেকে আবার গ্রাস করা যাবে না।
মার্কো টপলনিক

উত্তর:


24

আপনি যদি সীমাবদ্ধ সমান্তরাল ক্যাপাবিলিটি সহ বেঁচে থাকতে পারেন তবে নিম্নলিখিত সমাধানটি কাজ করবে:

private static <T> Stream<T> nonEmptyStream(
    Stream<T> stream, Supplier<RuntimeException> e) {

    Spliterator<T> it=stream.spliterator();
    return StreamSupport.stream(new Spliterator<T>() {
        boolean seen;
        public boolean tryAdvance(Consumer<? super T> action) {
            boolean r=it.tryAdvance(action);
            if(!seen && !r) throw e.get();
            seen=true;
            return r;
        }
        public Spliterator<T> trySplit() { return null; }
        public long estimateSize() { return it.estimateSize(); }
        public int characteristics() { return it.characteristics(); }
    }, false);
}

এটি ব্যবহার করে কিছু উদাহরণ কোড এখানে দেওয়া হয়েছে:

List<String> l=Arrays.asList("hello", "world");
nonEmptyStream(l.stream(), ()->new RuntimeException("No strings available"))
  .forEach(System.out::println);
nonEmptyStream(l.stream().filter(s->s.startsWith("x")),
               ()->new RuntimeException("No strings available"))
  .forEach(System.out::println);

(দক্ষ) প্যারালাল এক্সিকিউশনটির সমস্যাটি হ'ল প্রবন্ধকে সমর্থনকারী বিভক্তকরণের জন্য Spliteratorথ্রেড-নিরাপদ উপায়ে প্রয়োজন যাতে খণ্ড-নিরাপদ উপায়ে খণ্ডগুলির কোনও মান খুঁজে পাওয়া যায়। তারপরে কার্যকর tryAdvanceকরা টুকরোটির শেষ অংশটি বুঝতে হবে যে উপযুক্ত ব্যতিক্রমটি ছুঁড়ে ফেলার জন্য এটি সর্বশেষ (এবং এটিও অগ্রসর হতে পারেনি)। সুতরাং আমি এখানে বিভক্ত করার জন্য সমর্থন যোগ করিনি।


33

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

static <T> Stream<T> throwIfEmpty(Stream<T> stream) {
    Iterator<T> iterator = stream.iterator();
    if (iterator.hasNext()) {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
    } else {
        throw new NoSuchElementException("empty stream");
    }
}

static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Supplier<T> supplier) {
    Iterator<T> iterator = stream.iterator();
    if (iterator.hasNext()) {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
    } else {
        return Stream.of(supplier.get());
    }
}

মূলত একটি মধ্যে প্রবাহ ঘুরিয়ে Iteratorকল করার জন্য hasNext()এটি, এবং যদি সত্য, চালু Iteratorএকটি ফিরে Stream। এটি অকার্যকর যে স্ট্রিমের পরবর্তী সমস্ত ক্রিয়াকলাপ ইটারেটর hasNext()এবং next()পদ্ধতিগুলির মধ্য দিয়ে যাবে , যা এও বোঝায় যে স্ট্রিমটি কার্যকরভাবে ধারাবাহিকভাবে প্রক্রিয়াজাত করা হয়েছে (এমনকি পরে এটি সমান্তরাল হয়ে গেলেও)। তবে এটি আপনাকে স্ট্রিমের সমস্ত উপাদান বাফারিং ছাড়াই পরীক্ষা করার অনুমতি দেয়।

এটির Spliteratorপরিবর্তে একটি ব্যবহার করে সম্ভবত উপায় আছে Iterator। এই সম্ভাব্যভাবে প্রত্যাবর্তিত স্ট্রিমটি সমান্তরালভাবে চলমান সহ ইনপুট স্ট্রিমের মতো একই বৈশিষ্ট্যগুলির মঞ্জুরি দেয়।


4
আমি মনে করি না একটি রক্ষণীয় সমাধান দক্ষ সমান্তরাল প্রক্রিয়াজাতকরণ সমর্থন করবেন যেমন বিভাজন সমর্থন করা কঠিন আছে, তবে থাকার estimatedSizeএবং characteristicsএমনকি একক থ্রেডেড পারফরম্যান্সের উন্নতি হতে পারে। এটা ঠিক হয়েছিল যে Spliteratorআপনি Iteratorসমাধানটি পোস্ট করার সময় আমি সমাধানটি লিখেছিলাম ...
হোলার

4
আপনি স্ট্রিমিট্রেটারের জন্য স্ট্রিমটি জিজ্ঞাসা করতে পারেন, ট্রাইআডভান্স (ল্যাম্বদা) কল করতে পারেন যেখানে আপনার ল্যাম্বদা এতে যে কোনও কিছু কেটে নিয়েছে এবং তারপরে কোনও স্প্লিটেটরকে ফিরিয়ে আনবে যা অন্তর্নিহিত স্প্লিটরেটারকে প্রায় সবকিছু অর্পণ করে, ব্যতীত এটি প্রথম উপাদানটিকে প্রথম অংশে ফিরে আটকায় ( এবং অনুমানের আকার নির্ধারণ করে) fix
ব্রায়ান গোয়েট

4
@ ব্রায়ানগোয়েজ হ্যাঁ, এটি আমার ধারণা ছিল, আমি এখনও এই সমস্ত বিবরণ পরিচালনা করার জন্য পায়ে কাজ করতে বিরত হইনি।
স্টুয়ার্ট

4
@ ব্রায়ান গোয়েটস: এটিই "খুব জটিল" বলতে চাইছিলাম। এর tryAdvanceআগে Streamডাকা এটি অলস প্রকৃতিরটিকে Stream"আংশিক অলস" প্রবাহে রূপান্তরিত করে। এটি এও বোঝায় যে প্রথম উপাদানটির সন্ধান করা আর কোনও সমান্তরাল ক্রিয়াকলাপ নয় কারণ আপনাকে প্রথমে tryAdvanceবিভক্ত করতে হবে এবং প্রকৃত সমান্তরাল ক্রিয়াকলাপটি একই সাথে বিভক্ত অংশগুলিতে করতে হবে, যতদূর আমি বুঝতে পেরেছি। যদি একমাত্র টার্মিনাল অপারেশন হয় findAnyবা অনুরূপ যা পুরো parallel()অনুরোধটি ধ্বংস করে দেবে ।
হলগার

4
সুতরাং সম্পূর্ণ সমান্তরাল সমর্থনের জন্য আপনাকে tryAdvanceস্ট্রিমটি বলার আগে কল করতে হবে না এবং প্রতিটি বিভক্ত অংশকে একটি প্রক্সিতে আবদ্ধ করতে হবে এবং আপনার নিজের সমস্ত সাম্প্রতিক ক্রিয়াকলাপের "hasAny" তথ্য সংগ্রহ করতে হবে এবং নিশ্চিত করতে হবে যে শেষ সমবর্তী অপারেশনটি পছন্দসই ব্যতিক্রম ছুঁড়ে ফেলে যদি স্ট্রিমটি খালি ছিল। প্রচুর স্টাফ…
হোলার

25

এটি অনেক ক্ষেত্রে পর্যাপ্ত হতে পারে

stream.findAny().isPresent()

15

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

আপনি যেটা করতে পারেন সেরা তা দিয়ে স্ট্রিমটি সমাপ্ত করা findAny() টার্মিনাল অপারেশন , যা কোনও উপাদান খুঁজে পেলে বন্ধ হয়ে যায়, তবে যদি কিছুই না থাকে তবে এটি খুঁজে বার করার জন্য সমস্ত ইনপুট তালিকাটিতে পুনরাবৃত্তি করতে হবে।

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

আউটপুট তালিকা উত্পাদন করতে অবশ্যই আপনাকে একটি নতুন স্ট্রিম তৈরি করতে হবে।


7
আছে anyMatch(alwaysTrue()), আমি মনে করি এটি সবচেয়ে নিকটতম hasAny
মার্কো টপলনিক

4
@ মারকো টপলনিক কেবলমাত্র রেফারেন্সটি পরীক্ষা করেছেন - আমার মনে যা ছিল তা সন্ধানের জন্য (কোন) মিলছিল, যদিও যে কোনও ম্যাচ () কাজ করবে।
ইরান

4
anyMatch(alwaysTrue())পুরোপুরি আপনার এর অভিপ্রেত শব্দার্থবিদ্যা সাথে মিলে যায় hasAny, আপনি একটি দান booleanপরিবর্তে Optional<T>--- কিন্তু আমরা বিভাজন চুল এখানে :)
মার্কো Topolnik

4
নোট alwaysTrueএকটি পেয়ারা শিকারী।
জিন-ফ্রান্সোইস সাভার্ড

11
anyMatch(e -> true)তারপর।
এফবিবি

6

আমি মনে করি একটি বুলিয়ান ম্যাপ করার জন্য যথেষ্ট হওয়া উচিত

কোডটি হ'ল:

boolean isEmpty = anyCollection.stream()
    .filter(p -> someFilter(p)) // Add my filter
    .map(p -> Boolean.TRUE) // For each element after filter, map to a TRUE
    .findAny() // Get any TRUE
    .orElse(Boolean.FALSE); // If there is no match return false

4
আপনার যদি এটির প্রয়োজন হয় তবে কেনগ্ল্যাক্সনের উত্তর আরও ভাল।
ডোমিনিকাস মোস্তাউসকিস

এটি অকেজো, এটি সংগ্রহের প্রতিলিপি দেয়
Eএম্পটি

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

কারণ এটিও সদৃশStream.anyMatch()
ক্রিজিসেক

4

স্টুয়ার্টের ধারণার অনুসরণ করে এটি এর Spliteratorমতো করে করা যেতে পারে :

static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Stream<T> defaultStream) {
    final Spliterator<T> spliterator = stream.spliterator();
    final AtomicReference<T> reference = new AtomicReference<>();
    if (spliterator.tryAdvance(reference::set)) {
        return Stream.concat(Stream.of(reference.get()), StreamSupport.stream(spliterator, stream.isParallel()));
    } else {
        return defaultStream;
    }
}

আমি মনে করি এটি সমান্তরাল স্ট্রিমগুলির সাথে কাজ করে কারণ stream.spliterator()ক্রিয়াকলাপটি স্ট্রিমটিকে সমাপ্ত করবে এবং তারপরে এটি প্রয়োজনীয় হিসাবে পুনর্নির্মাণ করবে

আমার ব্যবহারের ক্ষেত্রে আমার Streamডিফল্ট মানের চেয়ে একটি ডিফল্ট প্রয়োজন । এটি আপনার পক্ষে যা প্রয়োজন তা না হলে এটি পরিবর্তন করা বেশ সহজ


এটি সমান্তরাল স্ট্রিমগুলির সাথে পারফরম্যান্সে উল্লেখযোগ্যভাবে প্রভাব ফেলবে কিনা তা আমি বুঝতে পারি না। সম্ভবত এটির পরীক্ষা করা উচিত যদি এটি প্রয়োজন হয়
ফিনিক্স7360

দুঃখিত যে বুঝতে পারিনি যে @ হোলজারের সাথে একটি সমাধানও ছিল Spliteratorআমি ভাবছি যে দু'জনের তুলনা কীভাবে হয়।
ফিনিক্স7360

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