Java 8 Streams-基于其他列表从嵌套列表中删除项

Java 8 Streams-基于其他列表从嵌套列表中删除项,java,java-8,java-stream,Java,Java 8,Java Stream,如何从集合中每个对象的列表属性中删除元素并返回相同的集合 class Student { String name; List<String> friends; // TODO constructor, getters and setters } Student chris = new Student("chris", new ArrayList<>(Arrays.asList("sean", "mike", "mary", "

如何从集合中每个对象的列表属性中删除元素并返回相同的集合

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"); 
我在初级阶段就在集合中使用了流,但嵌套让我感到困惑:

List<Student> studentsWithGoodFriends = students.stream()
    .map(student -> student.getFriends().flatMap(student -> student.stream())
    .filter(friend-> !badKids.contains(friend))
    .map(Student student -> new Student...

那我就迷路了。我熟悉返回按对象属性过滤的列表,但不过滤列表属性

您需要调整代码,至少先编译。像class之类的东西,如果大写C,即使逻辑正常,也会使代码崩溃。我尽量不做太多的改变,只是为了让它发挥作用,这样你就可以看到你哪里弄错了

我还提供了几个使用不同技术的示例,以便您可以根据需要查看涉及流的选项,还包括函数引用和更传统的引用。我希望有帮助

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 );
    }
}

您不需要流来完成此任务,特别是如果您想改变现有的好友列表:

students.forEach(s -> s.getFriends().removeAll(badKids));
就这样。这就使用了这个方法

要点:要使其工作,Student.getFriends方法返回的好友列表必须是可变的,例如ArrayList

尽管上面的解决方案很简洁,但它打破了封装,因为我们正在修改学生班级之外每个学生的朋友列表。要解决此问题,您需要向Student类添加一个方法:


例如,您可以通过以下方式执行此操作:

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 +
                    '}';
        }
    }
}
您可以这样做:

List<Students> good = st.stream()
        .peek(s -> s.getFriends().removeAll(bad))
        .collect(Collectors.toList());
但使用peek这种方式应该用来记录是一种反模式,您可以使用map代替

List<Students> good = st.stream()
        .map(s -> { 
             s.getFriends().removeAll(bad);
             return s;
        })
        .collect(Collectors.toList());

到目前为止您尝试了什么?请注意Arrays.asList创建了一个不可变的列表。如果您创建一个常规的ArrayList,则只需执行list.removeAllotherList;。这里不需要流,但需要正确的语法。目前代码很糟糕。Arrays.asList与当前问题无关,请将代码更改为使用ArrayList。谢谢!我一直在努力解决Arrays.asList的不变性问题,没有想到这个不使用字符串的函数示例。
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]}
List<Students> good = st.stream()
        .peek(s -> s.getFriends().removeAll(bad))
        .collect(Collectors.toList());
List<Students> good = st.stream()
        .map(s -> { 
             s.getFriends().removeAll(bad);
             return s;
        })
        .collect(Collectors.toList());