当前位置: 首页 > 面试题库 >

有没有办法比较lambda?

杨星纬
2023-03-14
问题内容

假设我有一个使用lambda表达式(闭包)定义的对象列表。有没有一种方法可以检查它们以便进行比较?

我最感兴趣的代码是

    List<Strategy> strategies = getStrategies();
    Strategy a = (Strategy) this::a;
    if (strategies.contains(a)) { // ...

完整的代码是

import java.util.Arrays;
import java.util.List;

public class ClosureEqualsMain {
    interface Strategy {
        void invoke(/*args*/);
        default boolean equals(Object o) { // doesn't compile
            return Closures.equals(this, o);
        }
    }

    public void a() { }
    public void b() { }
    public void c() { }

    public List<Strategy> getStrategies() {
        return Arrays.asList(this::a, this::b, this::c);
    }

    private void testStrategies() {
        List<Strategy> strategies = getStrategies();
        System.out.println(strategies);
        Strategy a = (Strategy) this::a;
        // prints false
        System.out.println("strategies.contains(this::a) is " + strategies.contains(a));
    }

    public static void main(String... ignored) {
        new ClosureEqualsMain().testStrategies();
    }

    enum Closures {;
        public static <Closure> boolean equals(Closure c1, Closure c2) {
            // This doesn't compare the contents 
            // like others immutables e.g. String
            return c1.equals(c2);
        }

        public static <Closure> int hashCode(Closure c) {
            return // a hashCode which can detect duplicates for a Set<Strategy>
        }

        public static <Closure> String asString(Closure c) {
            return // something better than Object.toString();
        }
    }    

    public String toString() {
        return "my-ClosureEqualsMain";
    }
}

似乎唯一的解决方案是将每个lambda定义为一个字段,并且仅使用这些字段。如果要打印出称为的方法,最好使用Method。lambda表达式有更好的方法吗?

此外,是否可以打印lambda并获得人类可读的内容?如果你打印this::a而不是

ClosureEqualsMain$$Lambda$1/821270929@3f99bd52

得到类似的东西

ClosureEqualsMain.a()

甚至使用this.toString和方法。

my-ClosureEqualsMain.a();

问题答案:

可以相对于规范或实现来解释这个问题。显然,实现可能会发生变化,但是你可能会愿意在这种情况下重写代码,因此我将同时回答这两个问题。

这也取决于你要做什么。你是要优化,还是要确保两个实例具有(或不具有)相同的功能?(如果是后者,你将发现自己与计算物理学不符,即使是简单的问题,例如询问两个函数是否计算相同的事物,也无法确定。)

从规范的角度来看,语言规范仅保证评估(而不是调用)lambda表达式的结果是实现目标功能接口的类的实例。它不保证结果的身份或混淆程度。这是设计使然,可为实现提供最大的灵活性以提供更好的性能(这是lambda可以比内部类更快的方式;我们不依赖内部类所具有的“必须创建唯一实例”约束)。

因此,基本上,该规范并没有给你带来太多好处,只是显然两个参考相等(==)的lambda将要计算相同的函数。

从实现的角度来看,你可以得出更多结论。在实现lambda的综合类与程序中的捕获位置之间存在(当前可能会更改)1:1的关系。因此,捕获“ x-> x + 1”的两个单独的代码位可以很好地映射到不同的类。但是,如果你在相同的捕获站点上评估相同的lambda,并且该lambda没有被捕获,则会得到相同的实例,可以将其与引用相等性进行比较。

如果你的Lambda可序列化,则它们会更轻松地放弃其状态,以换取牺牲一些性能和安全性(没有免费的午餐)。

调整相等性定义的一种实际可行方法是使用方法引用,因为这将使它们可以用作侦听器并正确地注销。这正在考虑中。

我认为你要尝试的是:如果将两个lambda转换为相同的功能接口,由相同的行为函数表示,并且具有相同的捕获arg,则它们是相同的

不幸的是,这既很难做到(对于不可序列化的lambda,你无法获得它的所有组件),而且还不够(因为两个单独编译的文件可以将同一lambda转换为相同的功能接口类型,而你不会无法分辨。)

EG讨论了是否公开足够的信息以做出这些判断,还讨论了lambda是否应实现更具选择性的equals / hashCode或更具描述性的toString。结论是,我们不愿意在性能成本上付出任何代价来使此信息可供调用者使用(权衡取舍,对99.99%的用户收取0.01%的好处而受到惩罚)。

尚未得出关于toString的明确结论,但有待将来进一步探讨。但是,双方在这个问题上都提出了一些好的论点。这不是灌篮。



 类似资料:
  • 假设我有一个使用lambda表达式(闭包)定义的对象列表。有没有办法检查它们以便进行比较? 我最感兴趣的代码是 完整的代码是 看来唯一的解决方案是将每个lambda定义为一个字段,并仅使用这些字段。如果要打印调用的方法,最好使用。使用lambda表达式有更好的方法吗? 此外,是否可以打印一个lambda并获得一些人类可读的内容?如果您打印而不是 得到类似于 甚至可以使用和方法。

  • 在java中,不能使用。compareTo()将基元类型(如int)与其他类型进行比较。 如果两个整数小于零,有没有办法比较?我试图将整数与数组中的点进行比较。 谢啦! 解决方法:抱歉,大家都有一秒钟的大脑抽筋。我忘记了a.compareTo(b)根据第一个值是否小于(返回-1)、等于(返回0)或大于(返回1)返回-1、0或1。我的代码是int.compareTo(数组)

  • 问题内容: 我目前正在使用以下功能比较字典值。有更快或更更好的方法吗? 编辑:两个字典包含相同的键。 问题答案: 如果字典具有相同的键集,并且您需要所有这些打印件以实现任何值差异,那么您将无能为力。也许像这样: 几乎等同于您所拥有的,但是您可能会得到更好的演示,例如在循环使用diffkey之前对其进行排序。

  • Spring HATEOAS提供了方便的ControllerLinkBuilder来创建指向控制器方法的链接,这些方法将作为HREF添加到返回给客户端的JSON/XML中。例如: ... 可能会生成类似以下内容的JSON: 然而... 我倾向于通过反向代理访问我的服务。我想大多数人可能会这样做。这让我可以在不同的端口上运行多个服务,但让我可以通过相同的基本URL访问它们。不幸的是,通过代理访问意味

  • 问题内容: 如何在Java中比较没有时间的日期? 此代码比较时间和日期! 问题答案: 尝试比较将时间更改为00:00:00的日期(使用此功能):

  • 问题内容: 请考虑以下表格: 部 员工 编写查询以返回人员总数为4或更多的那些部门的雇员的empname和deptname列。记录应按姓氏的字母顺序返回 这是我的看法: 您将如何对此进行改进? 问题答案: 这比较短,而且执行速度可能也更快 从分组开始。您不需要内部查询中的COUNT。然后,联接两个表只是为了获得名称。 *之所以使用 *INNER JOIN, 是因为一旦计数完成,我们已经知道 员工存