You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 8 Lambda优化:基于多层子列表过滤Event列表的最佳实践

问题描述

希望基于子列表的子列表条件过滤父列表:父类Event包含Band集合,Band集合包含Member集合,需要根据Member的名称返回对应的Event列表。

实体类定义

Event类

@Entity
public class Event {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;

    @ManyToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "event")
    private Set<Band> bands;
}

Band类

@Entity
public class Band {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "NAME")
    private Long id;

    @Column(name = "NAME")
    private String name;

    private Set<Member> members;
    // 省略其他代码
}

Member类

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME")
    String name;
    // 省略其他代码
}

当前实现代码

public List<Event> getFilteredEventsInJava(String query) {
    final List<Event> events = Optional.ofNullable(eventRepository.findAllBy()).orElseGet(Collections::emptyList).stream()
            .filter(e -> Optional.ofNullable(e.getBands()).orElseGet(Collections::emptySet).stream()
                    .filter(b ->  Optional.ofNullable(b.getMembers()).orElseGet(Collections::emptySet).stream().
                            filter(m -> m.getName().contains(query)).count()>0).count()>0)
            .collect(Collectors.toList());
    return events;
}

当前代码可正常运行,寻求优化建议。


优化建议

1. 用anyMatch替代count()>0,提升性能

count()会遍历整个流才能得到结果,而anyMatch()是短路操作——一旦找到符合条件的元素就会停止遍历,性能更优:

public List<Event> getFilteredEventsInJava(String query) {
    List<Event> events = Optional.ofNullable(eventRepository.findAllBy())
            .orElseGet(Collections::emptyList)
            .stream()
            .filter(e -> Optional.ofNullable(e.getBands())
                    .orElseGet(Collections::emptySet)
                    .stream()
                    .anyMatch(b -> Optional.ofNullable(b.getMembers())
                            .orElseGet(Collections::emptySet)
                            .stream()
                            .anyMatch(m -> m.getName().contains(query))))
            .collect(Collectors.toList());
    return events;
}

2. 提取辅助方法,提升代码可读性

嵌套的流操作可读性差,可以把判断逻辑提取成单独的方法:

public List<Event> getFilteredEventsInJava(String query) {
    return Optional.ofNullable(eventRepository.findAllBy())
            .orElseGet(Collections::emptyList)
            .stream()
            .filter(event -> hasMemberMatchingQuery(event, query))
            .collect(Collectors.toList());
}

private boolean hasMemberMatchingQuery(Event event, String query) {
    return Optional.ofNullable(event.getBands())
            .orElseGet(Collections::emptySet)
            .stream()
            .anyMatch(band -> hasMemberMatchingQuery(band, query));
}

private boolean hasMemberMatchingQuery(Band band, String query) {
    return Optional.ofNullable(band.getMembers())
            .orElseGet(Collections::emptySet)
            .stream()
            .anyMatch(member -> member.getName().contains(query));
}

3. 直接在数据库层过滤(最优方案)

当前代码是先把所有Event加载到内存再过滤,如果数据量很大,内存开销会很高,性能很差。建议直接在EventRepository中写JPQL或用Spring Data JPA的方法命名查询,让数据库完成过滤:

方法1:JPQL查询

在EventRepository中定义方法:

@Repository
public interface EventRepository extends JpaRepository<Event, Long> {
    @Query("SELECT DISTINCT e FROM Event e JOIN e.bands b JOIN b.members m WHERE m.name LIKE %:query%")
    List<Event> findByMemberNameContaining(@Param("query") String query);
}

然后业务方法简化为:

public List<Event> getFilteredEventsInJava(String query) {
    return eventRepository.findByMemberNameContaining(query);
}

方法2:Spring Data JPA方法命名

如果不想写JPQL,也可以用方法命名规则:

@Repository
public interface EventRepository extends JpaRepository<Event, Long> {
    List<Event> findDistinctByBandsMembersNameContaining(String query);
}

这样数据库会直接返回符合条件的Event,避免加载大量无关数据,性能提升非常明显。

4. 避免集合为null的初始化优化

可以在实体类中直接初始化空集合,这样就不需要每次用Optional处理null:
比如在Event类中:

@ManyToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "event")
private Set<Band> bands = new HashSet<>();

Band类中:

private Set<Member> members = new HashSet<>();

这样业务代码可以简化,不需要再用Optional.ofNullable(...).orElseGet(Collections::emptySet):

public List<Event> getFilteredEventsInJava(String query) {
    return eventRepository.findAllBy()
            .stream()
            .filter(event -> event.getBands().stream()
                    .anyMatch(band -> band.getMembers().stream()
                            .anyMatch(member -> member.getName().contains(query))))
            .collect(Collectors.toList());
}

内容的提问来源于stack exchange,提问作者user2565568

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:05:40