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
相关产品推荐
相关产品推荐

