我有一个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()
操作似乎正常工作,因为只返回了两个类别,即使三个匹配至少有三个短语但第二个没有按预期工作的标准。
我哪里错了?
您还可以找到此代码on 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 + "]";
}
}
}
答案 0 :(得分:3)
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()));
}
答案 1 :(得分:2)
在您的代码中,您基本上将限制应用于原始流两次。而不是这里
.flatMap(List::stream)
你应该在其上返回调用limit
的流:
.flatMap(d -> d.stream().limit(numberOfPhrasesPerCategory))