Java 8 complex custom collector

≯℡__Kan透↙ 提交于 2019-12-07 03:37:21

问题


I have a stream of objects which I would like to collect the following way.

Let's say we are handling forum posts:

class Post {
    private Date time;
    private Data data
}

I want to create a list which groups posts by a period. If there were no posts for X minutes, create new group.

class PostsGroup{
    List<Post> posts = new ArrayList<> ();
}

I want to get a List<PostGroups> containing the posts grouped by the interval.

example: interval 10 minutes.

posts:

[{time:x, data:{}}, {time:x + 3, data:{}} , {time:x+ 12, data:{}, {time:x + 45, data:{}}}]

I want to get a list of posts group:

[
 {posts : [{time:x, data:{}}, {time:x + 3, data:{}}, {time:x+ 12, data:{}]]},
{posts : [{time:x+ 45, data:{}]}
]
  • notice that the first group lasted till X + 22. Then a new post was received at X + 45.

Is this possible?


回答1:


This problem could be easily solved using the groupRuns method of my StreamEx library:

long MAX_INTERVAL = TimeUnit.MINUTES.toMillis(10);
StreamEx.of(posts)
        .groupRuns((p1, p2) -> p2.time.getTime() - p1.time.getTime() <= MAX_INTERVAL)
        .map(PostsGroup::new)
        .toList();

I assume that you have a constructor

class PostsGroup {
    private List<Post> posts;

    public PostsGroup(List<Post> posts) {
        this.posts = posts;
    }
}

The StreamEx.groupRuns method takes a BiPredicate which is applied to two adjacent input elements and returns true if they must be grouped together. This method creates the stream of lists where each list represents the group. This method is lazy and works fine with parallel streams.




回答2:


You need to retain state between stream entries and write yourself a grouping classifier. Something like this would be a good start.

class Post {

    private final long time;
    private final String data;

    public Post(long time, String data) {
        this.time = time;
        this.data = data;
    }

    @Override
    public String toString() {
        return "Post{" + "time=" + time + ", data=" + data + '}';
    }

}

public void test() {
    System.out.println("Hello");
    long t = 0;
    List<Post> posts = Arrays.asList(
            new Post(t, "One"),
            new Post(t + 1000, "Two"),
            new Post(t + 10000, "Three")
    );
    // Group every 5 seconds.
    Map<Long, List<Post>> gouped = posts
            .stream()
            .collect(Collectors.groupingBy(new ClassifyByTimeBetween(5000)));
    gouped.entrySet().stream().forEach((e) -> {
        System.out.println(e.getKey() + " -> " + e.getValue());
    });

}

class ClassifyByTimeBetween implements Function<Post, Long> {

    final long delay;
    long currentGroupBy = -1;
    long lastDateSeen = -1;

    public ClassifyByTimeBetween(long delay) {
        this.delay = delay;
    }

    @Override
    public Long apply(Post p) {
        if (lastDateSeen >= 0) {
            if (p.time > lastDateSeen + delay) {
                // Grab this one.
                currentGroupBy = p.time;
            }
        } else {
            // First time - start there.
            currentGroupBy = p.time;
        }
        lastDateSeen = p.time;
        return currentGroupBy;
    }

}


来源:https://stackoverflow.com/questions/34417261/java-8-complex-custom-collector

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!