StreamAPI java filter
Привет) У меня есть стрим с публикациями, сам класс публикации выглядит так:
public class Article {
private Long id;
private String title;
private String text;
private ArticleStatus status;
private Long authorId;
private Date createdAt;
private Date updatedAt;
private List<Tag> tags;
}
----------
public class Tag {
private Long id;
private String name;
private List<Article> articles;
}
И есть стрим с тегами в качестве входного параметра - простой Stream<String>
Мне необходимо отфильтровать стрим с публикациями по тегам (если он содержит хоть один перечисленный тег - отлично, подойдет). То есть, задействовать вместе Stream<Article> articles и Stream<String> tags
Подскажите, как это можно сделать?
Ответы (3 шт):
Как-то так
public static void main(String[] args) {
List<Tag> articlesTags1 = new ArrayList<Tag>();
articlesTags1.add(new Tag().setName("tag1"));
articlesTags1.add(new Tag().setName("tag2"));
articlesTags1.add(new Tag().setName("tag3"));
List<Tag> articlesTags2 = new ArrayList<Tag>();
articlesTags2.add(new Tag().setName("tag2"));
articlesTags2.add(new Tag().setName("tag3"));
articlesTags2.add(new Tag().setName("tag4"));
List<Tag> articlesTags3 = new ArrayList<Tag>();
articlesTags3.add(new Tag().setName("tag5"));
articlesTags3.add(new Tag().setName("tag6"));
articlesTags3.add(new Tag().setName("tag7"));
Stream<String> tags = Arrays.stream(new String[] {"tag1", "tag7"});
List<String> tagsList = tags.collect(Collectors.toList());
Stream<Articles> articles =
Stream.of(
new Articles[] {
new Articles().setTitle("title1").setTags(articlesTags1),
new Articles().setTitle("title2").setTags(articlesTags2),
new Articles().setTitle("title3").setTags(articlesTags3)
});
Stream<Articles> flteredArticles =
articles.filter(
a ->
a.getTags().stream()
.anyMatch(
tag ->
tagsList.stream()
.anyMatch(searchingTag -> searchingTag.equals(tag.getName()))));
flteredArticles.forEach(article -> System.out.println(article.getTitle()));
}
Поменяйте классы на такие
Tag.java
package test;
import java.util.List;
public class Tag {
private Long id;
private String name;
private List<Articles> articles;
/** @return the id */
public Long getId() {
return id;
}
/** @param id the id to set */
public Tag setId(Long id) {
this.id = id;
return this;
}
/** @return the name */
public String getName() {
return name;
}
/** @param name the name to set */
public Tag setName(String name) {
this.name = name;
return this;
}
/** @return the articles */
public List<Articles> getArticles() {
return articles;
}
/** @param articles the articles to set */
public Tag setArticles(List<Articles> articles) {
this.articles = articles;
return this;
}
}
и Articles.java
package test;
import java.sql.Date;
import java.util.List;
public class Articles {
private Long id;
private String title;
private String text;
private String status;
private Long authorId;
private Date createdAt;
private Date updatedAt;
private List<Tag> tags;
/** @return the id */
public Long getId() {
return id;
}
/** @param id the id to set */
public Articles setId(Long id) {
this.id = id;
return this;
}
/** @return the title */
public String getTitle() {
return title;
}
/** @param title the title to set */
public Articles setTitle(String title) {
this.title = title;
return this;
}
/** @return the text */
public String getText() {
return text;
}
/** @param text the text to set */
public Articles setText(String text) {
this.text = text;
return this;
}
/** @return the status */
public String getStatus() {
return status;
}
/** @param status the status to set */
public Articles setStatus(String status) {
this.status = status;
return this;
}
/** @return the authorId */
public Long getAuthorId() {
return authorId;
}
/** @param authorId the authorId to set */
public Articles setAuthorId(Long authorId) {
this.authorId = authorId;
return this;
}
/** @return the createdAt */
public Date getCreatedAt() {
return createdAt;
}
/** @param createdAt the createdAt to set */
public Articles setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
return this;
}
/** @return the updatedAt */
public Date getUpdatedAt() {
return updatedAt;
}
/** @param updatedAt the updatedAt to set */
public Articles setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
return this;
}
/** @return the tags */
public List<Tag> getTags() {
return tags;
}
/** @param tags the tags to set */
public Articles setTags(List<Tag> tags) {
this.tags = tags;
return this;
}
}
Вы слишком путано формулируете вопрос. Если я вас правильно понял, то есть какое-то количество публикаций с вложенными тегами, а также есть стринговый список названий тегов. Нужно отфильтровать те публикации, в которых вложнные теги совпадают по имени (поле name) со стринговым списком тегов.
Во-первых, если вы решаете такую задачу, то вы уже на неправильном пути. Такие операции должны быть возложены на бд (причем оптимизировать поиск в бд довольно просто - его можно свести исключительно к поиску по первичным ключам). Почему так? Представьте, что у вас миллионы публикация и не меньшее количество тегов. Вы все загрузите в память и будете итерироваться? Тогда вам никакой памяти не хватит. Подумайте над этим...
Что касается решения задачи (чисто теоретически), я бы добавил простой метод в Article. Так код будет лучше читаться в отличии от стрима в стриме. Ну и для списка стринговых тегов я бы все таки использовал сет, а не другую коллекцию.
public class Article {
private Long id;
private String title;
private String text;
//private ArticleStatus status;
private Long authorId;
private Date createdAt;
private Date updatedAt;
private List<Tag> tags;
public boolean contain(Set<String> tags) {
return this.tags.stream().anyMatch(tag -> tags.contains(tag.getName()));
}
}
public class Tag {
private Long id;
private String name;
private List<Article> articles;
}
public class Main {
public List<Article> filter(List<Article> articles, Set<String> tags) {
return articles.stream()
.filter(art -> art.contain(tags))
.collect(Collectors.toList());
}
}
Stream<Article> arts /* = Stream.of(new Article()) */;
Stream<String> tags /* = Stream.of("what", "ever") */;
стрим можно проходить только один раз, поэтому тэги придется собрать в коллекцию
Set<String> tagNames = tags.collect(Collectors.toSet());
тогда фильтр будет выглядеть как то так:
arts.filter(a -> a.tags.stream()
.map(t -> t.name)
.anyMatch(n -> tagNames.contains(n)));