【问题标题】:How to search multiple field in List using Java如何使用Java搜索列表中的多个字段
【发布时间】:2021-04-27 14:07:30
【问题描述】:

我有以下对象的列表:

public class OptionDetailResponse {
    private long id;

    private String flavor;
    private String size;
    private String status;
    private String barcode;
}

我想根据所有 4 个字段(id 除外)在这些对象的列表中搜索:

  • flavor(来自组合框的输入)
  • size(来自组合框的输入)
  • status(来自组合框的输入)
  • barcode(来自文本字段的输入)

这是我的带有 4 个输入字段的 UI:

我尝试了什么

我尝试使用Predicate<OptionDetailResponse> 进行搜索:

Predicate<OptionDetailResponse> selectFlavor = e -> e.getParentName().equals(flavor);

Predicate<OptionDetailResponse> selectSize = e -> e.getName().equals(size);

Predicate<OptionDetailResponse> selectStatus = e -> e.getStatus().equals(status);

Predicate<OptionDetailResponse> inputBarcode = e -> e.getBarcode().contains(barcode);

List<OptionDetailResponse> list = responseList.stream().filter(
      selectFlavor.and(selectSize).and(selectStatus).and(inputBarcode))
      .collect(Collectors.<OptionDetailResponse>toList());

但是当在所有搜索字段中选择一个值时,列表只返回正确的结果。

问题

  1. 如何在所有字段为空时使用 Predicate 获取所有列表?
  2. 还有其他方法可以按多个字段进行搜索吗?

【问题讨论】:

  • 您无法提前知道谓词是否匹配,但您可以在收集列表后检查:list = list.size() == 0 ? responseList: list;。此外,如果这与您的情况相关,您应该返回一份防御性副本:List.copyOf(responseList)
  • 2.复合谓词的有用资源stackoverflow.com/questions/24553761/…

标签: java search java-8 java-stream predicate


【解决方案1】:

我认为您可以检查可空性或不应该在每个谓词中检查的特定值,具体取决于您在未选择字段中的值。我认为它可以是这样的:

Predicate<OptionDetailResponse> selectFlavor = e -> flavor == null || e.getParentName().equals(flavor);

Predicate<OptionDetailResponse> selectFlavor = e -> flavor.equals("your unselected flavor value") || e.getParentName().equals(flavor);

.. 其他谓词也一样。

【讨论】:

    【解决方案2】:

    请记住,当您在 filter 方法中使用谓词时,结果将是与所提供谓词的“测试”操作相匹配的元素列表。
    您所做的是在逻辑 AND 中创建一个谓词链。这意味着过滤器的结果将是匹配所有给定谓词的元素列表。
    如果您需要不同的结果,您可以通过使用特定的 Predicate 函数来创建您的链,从而最终实现更复杂的条件。
    除了and(Predicate&lt;&gt; target),比如你有以下方法:

    • or(Predicate&lt;&gt; target):两个谓词之间的短路逻辑或
    • negate():当前 Predicate 实例的逻辑否定
    • not(Predicate&lt;&gt; target):返回一个谓词,它是提供的谓词的否定

    【讨论】:

      【解决方案3】:

      您可能想在每个字段上使用以下过滤器:

      • 如果搜索参数不是由 UI 提供(或留空),则不应用谓词:表示谓词匹配所有对象,无论对象的字段值如何
      • 如果搜索参数由 UI 提供(或不为空),则应用谓词

      也就是说,您可以在流中使用 从所有填充的输入字段组合的过滤器

      // renamed your stream-result variable to indicate that it was filtered
      List<OptionDetailResponse> filteredResult = responseList.stream()
            .filter( buildPredicateFromInputFields(flavor, size, status, barcode) )
            .collect(Collectors.toList());
      

      作为参数传递给 filter 的谓词由 4 个字段组合而成:

      // you could name the method more specific: matchesAllNonEmptyInputs
      Predicate<OptionDetailResponse> buildPredicateFromInputFields(
          String flavor,
          String size,
          String status,
          String barcode
      ) {
        // build a set of field-matchers (predicate) based on given input fields
        // null or empty (or blank) fields are excluded from the set
        var givenFieldPredicates = new ArrayList<Predicate<OptionDetailResponse>>(4); // max 4 entries 
      
        if (flavor != null && !flavor.isBlank()) {
            givenFieldPredicates.add(obj -> flavor.equals(obj.flavor))
        }
       
        if (size != null && !size.isBlank()) {
            givenFieldPredicates.add(obj -> size.equals(obj.size))
        }
      
        if (status != null && !status.isBlank()) {
            givenFieldPredicates.add(obj -> status.equals(obj.size))
        }
      
        // contained (partial match allowed)
        if (barcode != null && !barcode.isBlank()) {
            // will throw NullPointerException if object has null barcode!
            givenFieldPredicates.add(obj -> obj.barcode.contains(barcode))
        }
      
        // combined them using AND: each field predicate must match
        return givenFieldPredicates.stream().reduce(x -> true, Predicate::and);
      }
      

      另见:

      【讨论】:

        【解决方案4】:

        我们可以使用 Function、BiFunction 和方法引用以及 pojo 来过滤字段列表以构建类似的东西

            @Value
            public static class Filter<T> {
                private Function<OptionDetailResponse, T> getter;
                private BiFunction<T, T, Boolean> filter;
        
                public Predicate<OptionDetailResponse> toPredicate(OptionDetailResponse criteria) {
                    return o -> filter.apply(getter.apply(o), getter.apply(criteria));
                }
            }
        
            public static List<Filter<?>> filters() {
                List<Filter<?>> filterList = new ArrayList<>();
                filterList.add(new Filter<>(OptionDetailResponse::getFlavor, Object::equals));
                filterList.add(new Filter<>(OptionDetailResponse::getSize, Object::equals));
                filterList.add(new Filter<>(OptionDetailResponse::getStatus, Object::equals));
                filterList.add(new Filter<>(OptionDetailResponse::getBarcode, String::contains));
                return filterList;
            }
        
            public static final List<Filter<?>> FILTERS = filters();
        
            public Predicate<OptionDetailResponse> buildPredicate(OptionDetailResponse searchCriteria) {
        
                return FILTERS
                        .stream()
                        .filter(f -> f.getGetter().apply(searchCriteria) != null)
                        .map(f -> f.toPredicate(searchCriteria))
                        .reduce(o -> true, Predicate::and);
            }
        
        
            public List<OptionDetailResponse> search(List<OptionDetailResponse> responseList,
                                                     OptionDetailResponse searchCriteria) {
        
                return responseList.stream()
                        .filter(buildPredicate(searchCriteria))
                        .collect(Collectors.toList());
            }
        

        【讨论】:

          猜你喜欢
          • 2019-12-08
          • 2015-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多