【问题标题】:Google Collections Suppliers and FindGoogle Collections 供应商和查找
【发布时间】:2023-03-27 09:08:01
【问题描述】:

我正在寻找一种 Google Collections 方法,它返回不返回 null 的供应商序列的第一个结果。

我正在考虑使用 Iterables.find() 但在我的谓词中我必须调用我的供应商以将结果与 null 进行比较,然后在 find 方法返回供应商后再次调用它。

【问题讨论】:

    标签: java guava


    【解决方案1】:

    鉴于您对 Calm Storm 的回答的评论(不想两次致电 Supplier.get()),那么:

    private static final Function<Supplier<X>, X> SUPPLY = new Function<....>() {
        public X apply(Supplier<X> in) {
            // If you will never have a null Supplier, you can skip the test;
            // otherwise, null Supplier will be treated same as one that returns null
            // from get(), i.e. skipped
            return (in == null) ? null : in.get();
        }
    }
    

    然后

    Iterable<Supplier<X>> suppliers = ... wherever this comes from ...
    
    Iterable<X> supplied = Iterables.transform(suppliers, SUPPLY);
    
    X first = Iterables.find(supplied, Predicates.notNull());
    

    请注意,来自Iterables.transform() 的 Iterable 是惰性求值的,因此当Iterables.find() 循环遍历它时,您只求值到第一个非null 返回的值,并且只求值一次。

    【讨论】:

    • +1 很好地回答了这个问题。不过,我认为提出的问题应该是“最好的方法是什么”,而不是“什么是使用谷歌收藏的方法”,所以我提供了另一个答案。
    • 接受这个,因为它回答了原始问题,但也阅读了凯文的 cmets。
    【解决方案2】:

    您询问了如何使用 Google 收藏集来执行此操作,但以下是您在不使用 Google 收藏集的情况下执行此操作的方法。将其与 Cowan 的答案(这是一个很好的答案)进行比较——哪个更容易理解?

    private static Thing findThing(List<Supplier<Thing>> thingSuppliers) {
      for (Supplier<Thing> supplier : thingSuppliers) {
        Thing thing = supplier.get();
        if (thing != null) {
          return thing;
        }
      }
      // throw exception or return null
    }
    

    代替注释——如果这是你的类的调用者的错,适当地抛出 IllegalArgumentException 或 IllegalStateException;如果这不应该发生,请使用 AssertionError;如果调用 this 的代码需要检查它是正常发生的,您可能会返回 null。

    【讨论】:

    • 这是我最初的解决方案,我真的应该在问题中发布它。但是,由于我们一直在为 Predicates 和 Functions 找到一些不错的用途,所以我想看看是否存在像 Suppliers.firstSupplied 这样的东西。正如 Cowan 的(工作)建议所示,这可能是 Java 中的函数式风格导致代码难以理解的情况。两种解决方案都通过了测试。
    【解决方案3】:

    这有什么问题?

    List<Supplier> supplierList = //somehow get the list
    Supplier s = Iterables.find(supplierList, new Predicate<Supplier>(){
         boolean apply(Supplier supplier) {
             return supplier.isSomeMethodCall() == null;
         }
         boolean equals(Object o) {
             return false;
         }
    });
    

    您是否要保存一些行?我能想到的唯一优化是静态导入查找,这样你就可以摆脱“Iterables”。谓词也是一个匿名内部类,如果你在多个地方需要它,你可以创建一个类,它看起来像,

    List<Supplier> supplierList = //somehow get the list
    Supplier s = find(supplierList, new SupplierPredicateFinder());
    

    SupplierPredicateFinder 是另一个类。

    更新:在这种情况下 find 是错误的方法。您实际上需要一个这样的自定义函数,它可以返回两个值。如果您使用的是 commons-collections,那么您可以使用 DefaultMapEntry,或者您可以简单地返回 Object[2] 或 Map.Entry。

    public static DefaultMapEntry getSupplier(List<Supplier> list) {
        for(Supplier s : list) {
            Object heavyObject = s.invokeCostlyMethod();
            if(heavyObject != null) {
                 return new DefaultMapEntry(s, heavyObject);
            }
        }
    }
    

    用大小为 2 的列表或大小为 1 的哈希图或长度为 2 的数组替换 DefaultMapEntry :)

    【讨论】:

    • 在供应商(一个谷歌收藏)接口上有一个名为 get() 的方法。这是一个耗时的计算。 Iterables.find 在谓词匹配时返回 Supplier,而不是 get() 的结果。所以在 find() 的结果之后,我需要再次调用 get ,这需要再次执行计算。我可以使用 MemoizingSupplier,但如果 find 方法返回的是供应商的结果而不是供应商本身的结果会更好。
    • 这个答案不连贯。根据原始问题的文本,前半部分无效。后半部分,为什么用户只想要heavyObject,却同时返回supplier和heavyObject?我很担心我们会这么快就向任何人推荐 Map.Entry/Object[2] hack(这是一个怪诞的 hack),除非真的需要。
    • 我觉得我应该继续解释更多,因为我投了反对票:同样,这个问题是关于 gc 的 Supplier 类的,你似乎从来没有看过它(你会发现它是通用的并有一个名为 get()) 的方法。并且您的最后一个方法没有编译(最后预期返回语句)。我只是说所有这些小事加起来就是一个不是很有用的答案。
    • “这个问题是专门关于 gc 的供应商类的” - 提问者没有明确说明。我只是假设这是他的域类。 “而且你的最后一个方法无法编译” - 我试图展示一个通用结构,而不是可以粘贴到 IDE 中的现成代码“根据原始问题的文本,前半部分无效。” - 这个问题是在我回答之后编辑的(请参阅 Cowan 的回答和我的参考资料)
    猜你喜欢
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多