Java 8 Streams - 根据其他列表

时间:2018-03-14 20:17:15

标签: java java-8 java-stream

如何从集合中每个对象的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...

然后我迷路了。我熟悉返回由对象属性过滤的列表,但不过滤列表属性。

4 个答案:

答案 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());