我有一个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
预期产出:
实际产出:
请注意,第四运动队包括在内,即使我只要求每个类别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 + "]";
}
}
}发布于 2017-09-14 17:30:00
在看到给定数量的元素后,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()));
}发布于 2017-09-14 17:28:08
在您的代码中,您基本上是对原始流应用了两次限制。取而代之的是这里
.flatMap(List::stream)您应该返回调用limit的流:
.flatMap(d -> d.stream().limit(numberOfPhrasesPerCategory))https://stackoverflow.com/questions/46224931
复制相似问题