如何从集合中每个对象的List<String>
属性中删除元素并返回相同的集合?
class Student {
String name;
List<String> friends;
// TODO constructor, getters and setters
}
Student chris = new Student("chris",
new ArrayList<>(Arrays.asList("sean", "mike", "mary", "mark")));
Student tim = new Student("tim",
new ArrayList<>(Arrays.asList("mike", "john", "steve", "mary")));
List<Student> students = new ArrayList<>(Arrays.asList(chris, tim));
List<String> badKids = new ArrayList("mike", "sean");
我希望使用students
列表返回friends
而不使用badKids
使用流。这可能吗?返回对象将是(为清晰起见使用JSON):
[
{ name: "chris", friends: ["mary", "mark"] },
{ name: "tim", friends: ["john", "steve", "mary"]
]
我在初学者级别上使用过集合流,但嵌套让我感到困惑:
List<Student> studentsWithGoodFriends = students.stream()
.map(student -> student.getFriends().flatMap(student -> student.stream())
.filter(friend-> !badKids.contains(friend))
.map(Student student -> new Student...
然后我迷路了。我熟悉返回由对象属性过滤的列表,但不过滤列表属性。
答案 0 :(得分:3)
您不需要此任务的流,特别是如果您想要就地改变朋友列表:
students.forEach(s -> s.getFriends().removeAll(badKids));
就是这样。这使用Collection.removeAll
方法。
重要提示:为此,Student.getFriends()
方法返回的朋友列表必须是可变的,例如ArrayList
。
尽管上述解决方案简洁明了,但它打破了封装,因为我们正在改变Student
类之外的每个学生的朋友列表。要解决此问题,您需要向Student
类添加方法:
void removeBadKids(List<String> badKids) {
friends.removeAll(badKids);
}
因此,解决方案现在将成为:
students.forEach(s -> s.removeBadKids(badKids));
答案 1 :(得分:0)
您需要调整代码至少先编译。如果大写字母C会使你的代码破坏,即使你的逻辑有效,也会像类一样。我试着不要改变太多,只是为了让它发挥作用,所以你可以看到你弄错了。
我还包含了几个不同技术的示例,因此您可以根据需要查看涉及Streams的选项,还可以查看函数引用和更传统的选项。我希望它有所帮助。
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
class Student {
String name;
List< String > friends;
public Student( String name, List< String > friends ) {
this.name = name;
this.friends = friends;
}
public void removeFriendsIf( Predicate< String > test ) { // for example 5
getFriends( ).removeIf( test );
}
public List< String > getFriends( ) { // for example 4 -> students.forEach( e -> e.getFriends().removeIf( badKids::contains ) );
return friends;
}
public void removeFriends( List< String > badFriends ) { // no need for example here
getFriends( ).removeAll( badFriends );
}
}
class Initech {
public static void main( String[] reports ) {
Student chris = new Student( "chris", Arrays.asList( "sean", "mike", "mary", "mark" ) );
Student tim = new Student( "tim", Arrays.asList( "mike", "john", "steve", "mary" ) );
Student other = new Student( "tim", Arrays.asList( "john", "steve", "mary" ) );
List< Student > students = new ArrayList<>( );
students.add( chris );
students.add( tim );
students.add( other );
List< String > badKids = Arrays.asList( "mike", "sean" );
// Example 1 ----
// All students that do not have any of the bad friends
List< Student > studentsWithOnlyGoodFriends = students.stream( )
.filter( e -> e.friends.stream( )
.noneMatch( badKids::contains ) )
.collect( Collectors.toList( ) );
studentsWithOnlyGoodFriends.stream( )
.map( e -> e.friends )
.forEach( System.out::println );
System.out.println( );
// Example 2 ----
// All students but removing the bad apples
List< Student > studentsLostBadFriends = students.stream( )
.peek( e -> e.friends = e.friends.stream( )
.filter( f -> !badKids.contains( f ) )
.collect( Collectors.toList( ) ) )
.collect( Collectors.toList( ) );
studentsLostBadFriends.stream( )
.map( e -> e.friends )
.forEach( System.out::println );
System.out.println( );
//Example 3 ----
// The same as 2, without streams and with ArrayLists
chris = new Student( "chris", new ArrayList<>( Arrays.asList( "sean", "mike", "mary", "mark" ) ) );
tim = new Student( "tim", new ArrayList<>( Arrays.asList( "mike", "john", "steve", "mary" ) ) );
other = new Student( "tim", new ArrayList<>( Arrays.asList( "john", "steve", "mary" ) ) );
students.add( chris );
students.add( tim );
students.add( other );
students.forEach( e -> e.friends.removeIf( badKids::contains ) );
students.stream( )
.map( e -> e.friends )
.forEach( System.out::println );
//Example 4 ----
// The same as 3, without streams and with ArrayLists and the getter methods
chris = new Student( "chris", new ArrayList<>( Arrays.asList( "sean", "mike", "mary", "mark" ) ) );
tim = new Student( "tim", new ArrayList<>( Arrays.asList( "mike", "john", "steve", "mary" ) ) );
other = new Student( "tim", new ArrayList<>( Arrays.asList( "john", "steve", "mary" ) ) );
students.add( chris );
students.add( tim );
students.add( other );
students.forEach( e -> e.getFriends( )
.removeIf( badKids::contains ) );
students.stream( )
.map( e -> e.friends )
.forEach( System.out::println );
//Example 5 ----
// The same as 4, without streams and with ArrayLists and the getter methods
chris = new Student( "chris", new ArrayList<>( Arrays.asList( "sean", "mike", "mary", "mark" ) ) );
tim = new Student( "tim", new ArrayList<>( Arrays.asList( "mike", "john", "steve", "mary" ) ) );
other = new Student( "tim", new ArrayList<>( Arrays.asList( "john", "steve", "mary" ) ) );
students.add( chris );
students.add( tim );
students.add( other );
students.forEach( e -> e.removeFriendsIf( badKids::contains ) );
students.stream( )
.map( e -> e.friends )
.forEach( System.out::println );
}
}
答案 2 :(得分:0)
你可以这样做:
package demo;
import java.util.List;
import java.util.function.Predicate;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.*;
public class Example {
public static void main(String[] args) {
List<Student> students = asList(getChris(), getTim());
List<String> badFriends = asList("mike", "sean");
List<Student> cleanedStudentList = students.stream()
.map(student -> cleanBadFriendOnStudent(student, badFriends))
.collect(toList());
cleanedStudentList.forEach(System.out::println);
}
private static Student cleanBadFriendOnStudent(Student student, List<String> badKids) {
List<String> cleanedFriendList = student.friends.stream()
.filter(not(badKids::contains))
.collect(toList());
return new Student(student.name, cleanedFriendList);
}
private static <T> Predicate<T> not(Predicate<T> predicate) {
return predicate.negate();
}
private static Student getTim() {
return new Student("tim", asList("mike", "john", "steve", "mary"));
}
private static Student getChris() {
return new Student("chris", asList("sean", "mike", "mary", "mark"));
}
private static class Student {
private final String name;
private final List<String> friends;
Student(String name, List<String> friends) {
this.name = name;
this.friends = friends;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", friends=" + friends +
'}';
}
}
}
输出:
Student{name='chris', friends=[mary, mark]}
Student{name='tim', friends=[john, steve, mary]}
答案 3 :(得分:0)
你可以这样做:
List<Students> good = st.stream()
.peek(s -> s.getFriends().removeAll(bad))
.collect(Collectors.toList());
但是以这种方式使用peek(应该用于记录)是一种反模式,你可以改用地图。
List<Students> good = st.stream()
.map(s -> {
s.getFriends().removeAll(bad);
return s;
})
.collect(Collectors.toList());