繁体   English   中英

带有嵌套列表的Java流上的Limit()不适用

[英]Limit() on Java stream with nested list not applying

我有一个Category列表,每个Category都有一个Phrase列表,我想将此列表缩减为短语数量最少的类别,将短语数量限制为Y ,然后为所有对象创建一个新的对象CategoryAndPhrase匹配的类别和短语。

这似乎是Java 8流API的不错选择,但我没有得到我期望的结果。 这是我的代码,每行旁边都有注释,解释了我要实现的目标。 在该问题的末尾有完整的代码示例。

List<CategoryAndPhrase> categoriesAndPhrases = allCategories.stream()
            .filter(category -> category.getPhrases().size() >= numberOfPhrasesPerCategory) // remove the categories which don't have enough phrases to match our criteria
            .limit(numberOfCategories) // reduce the list to the number categories we require
            .map(Category::getPhrases) // change the stream so it is now a stream of the phrases for the selected categories
            .limit(numberOfPhrasesPerCategory) // reduce the phrases for each of the selected categories to the number of phrases we require
            .flatMap(List::stream)
            .map(phrase -> new CategoryAndPhrase(phrase.getCategory(), phrase.getName())) // create the new object for each of the selected phrases
            .collect(Collectors.toCollection(ArrayList::new));

输入数据:

  • 运动队<-- Category
    • 曼联<-- Phrase
    • 兵工厂
    • 斯旺西
    • 哈特尔普联
  • 问候
    • 你好
  • 再见
    • 再见
    • 阿迪奥斯
    • 金库
  • 语言能力
    • 英语
    • 义大利文
    • 法文
    • 德语

类别数:2

每个类别的短语数:3

预期产量:

  • 类别:[运动队],词组:[曼彻斯特联]
  • 类别:[运动队],词组:[阿森纳]
  • 类别:[运动队],词组:[斯旺西]
  • 类别:[再见],词组:[再见]
  • 类别:[再见],词组:[Adios]
  • 类别:[再见],词组:[Au revoir]

实际输出:

  • 类别:[运动队],词组:[曼彻斯特联]
  • 类别:[运动队],词组:[阿森纳]
  • 类别:[运动队],词组:[斯旺西]
  • 类别:[运动队],词组:[哈特尔普尔联]
  • 类别:[再见],词组:[再见]
  • 类别:[再见],词组:[Adios]
  • 类别:[再见],词组:[Au revoir]

请注意,即使我只要求每个类别使用3个短语,也包括第四运动队。

第一个limit()操作似乎可以正常工作,因为即使返回的两个类别中至少有三个符合至少三个词组的标准,但第二个类别却未按预期工作。

我要去哪里错了?

完整代码

您也可以在JDoodle上找到此代码。

import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.LinkedList;
import java.util.ArrayList;

public class MyClass {
    public static void main(String args[]) {
        new MyClass().run();
    }

    public void run() {
        Phrase sportsTeam1 = new Phrase("Manchester United", "Sports Teams");
        Phrase sportsTeam2 = new Phrase("Arsenal", "Sports Teams");
        Phrase sportsTeam3 = new Phrase("Swansea", "Sports Teams");
        Phrase sportsTeam4 = new Phrase("Hartlepool United", "Sports Teams");
        Category sportsTeams = new Category("Sports Teams", Arrays.asList(sportsTeam1, sportsTeam2, sportsTeam3, sportsTeam4));

        Phrase greeting1 = new Phrase("Hello", "Greetings");
        Category greetings = new Category("Greetings", Arrays.asList(greeting1));

        Phrase goodbye1 = new Phrase("Goodbye", "Goodbyes");
        Phrase goodbye2 = new Phrase("Adios", "Goodbyes");
        Phrase goodbye3 = new Phrase("Au revoir", "Goodbyes");
        Category goodbyes = new Category("Goodbyes", Arrays.asList(goodbye1, goodbye2, goodbye3));

        Phrase language1 = new Phrase("English", "Languages");
        Phrase language2 = new Phrase("Italian", "Languages");
        Phrase language3 = new Phrase("French", "Languages");
        Phrase language4 = new Phrase("German", "Languages");
        Category languages = new Category("Languages", Arrays.asList(language1, language2, language3, language4));

        List<Category> allCategories = Arrays.asList(sportsTeams, greetings, goodbyes, languages);
        int numberOfCategories = 2;
        int numberOfPhrasesPerCategory = 3;

        List<CategoryAndPhrase> categoriesAndPhrases = allCategories.stream()
                .filter(category -> category.getPhrases().size() >= numberOfPhrasesPerCategory) // remove the categories which don't have enough phrases to match our criteria
                .limit(numberOfCategories) // reduce the list to the number categories we require
                .map(Category::getPhrases) // change the stream so it is now a stream of the phrases for the selected categories
                .limit(numberOfPhrasesPerCategory) // reduce the phrases for each of the selected categories to the number of phrases we require
                .flatMap(List::stream)
                .map(phrase -> new CategoryAndPhrase(phrase.getCategory(), phrase.getName())) // create the new object for each of the selected phrases
                .collect(Collectors.toCollection(ArrayList::new));

        categoriesAndPhrases.forEach(categoryAndPhrase -> System.out.println(categoryAndPhrase));
    }

    class Category {

        private final String name;
        private final List<Phrase> phrases;

        public Category(final String name, final List<Phrase> phrases) {
            this.name = name;
            this.phrases = phrases;
        }

        public String toString() {
            return name;
        }

        public String getName() {
            return name;
        }

        public List<Phrase> getPhrases() {
            return phrases;
        }
    }

    class Phrase {

        private final String name;
        private final String category;

        public Phrase(final String name, final String category) {
            this.name = name;
            this.category = category;
        }

        public String toString() {
            return name;
        }

        public String getName() {
            return name;
        }

        public String getCategory() {
            return category;
        }
    }

    class CategoryAndPhrase {

        private final String category;
        private final String phrase;

        public CategoryAndPhrase(final String category, final String phrase) {
            this.category = category;
            this.phrase = phrase;
        }

        public String toString() {
            return "Category: [" + category + "], Phrase: [" + phrase + "]";
        }
    }
}

在看到给定数量的元素之后, limit()停止处理流元素。

这是您的代码实际执行的操作:

List<CategoryAndPhrase> categoriesAndPhrases = allCategories.stream()
        .filter(category -> category.getPhrases().size() >= numberOfPhrasesPerCategory) // remove the categories which don't have enough phrases to match our criteria
        .limit(numberOfCategories) // reduce the list to the number categories we require
        .map(Category::getPhrases) // transforms the Stream<Category> into a Stream<List<Phrase>>, where each category of the original stream is "replaced" by its list of phrases.
        .limit(numberOfPhrasesPerCategory) // only process N lists among all the lists of phrases

相反,您应该做的是:

List<CategoryAndPhrase> categoriesAndPhrases = allCategories.stream()
        .filter(category -> category.getPhrases().size() >= numberOfPhrasesPerCategory) // remove the categories which don't have enough phrases to match our criteria
        .limit(numberOfCategories) // reduce the list to the number categories we require
        .flatMap(category -> this.createCategoryAndPhrases(category, numberOfPhrasesPerCategory))
        .collect(Collectors.toList());

并且createCategoryAndPhrases方法应类似于:

private Stream<CategoryAndPhrase> createCategoryAndPhrases(Category category, int maxNumberOfPhrasesPerCategory) {
    return category.getPhrases().stream()
        .limit(maxNumberOfPhrasesPerCategory)
        .map(phrase -> new CategoryAndPhrase(category, phrase.getName()));
}

在您的代码中,您基本上将限制两次应用于原始流。 代替这里

.flatMap(List::stream)

您应该返回流调用limit

.flatMap(d -> d.stream().limit(numberOfPhrasesPerCategory))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM