【问题标题】:Java 8 - store lambdas in ListJava 8 - 在列表中存储 lambda
【发布时间】:2018-04-09 19:18:24
【问题描述】:

我想知道是否可以将 lambdas 存储在某个容器中,例如。 ArrayList 或 HashMap。 我想更改该代码:

public enum OPCODE implements BinaryOperator<Integer> {
    MOV((x, y) -> y),
    INC((x, y) -> ++x),
    DEC((x, y) -> --x),
    ADD((x, y) -> x + y),
    SUB((x, y) -> x - y);

    private final BinaryOperator<Integer> binaryOperator;

    OPCODE(BinaryOperator<Integer> binaryOperator) {
        this.binaryOperator = binaryOperator;
    }  

    @Override
    public Integer apply(Integer integer, Integer integer2) {
        return binaryOperator.apply(integer, integer2);
    }
}

类似于:

List<BinaryOperator<Integer>> opcodes = new ArrayList<BinaryOperator<Integer>>(){
    ((x, y) -> y),
    ((x, y) -> ++x)
};

等等

并像这样使用它:

opcodes[0].apply(a, b);

有可能吗?

【问题讨论】:

  • 附带说明,您的操作INCDEC 可能没有预期的效果,因为Java 是按值调用的,因此修改参数不会更改调用方的任何值,所以(x, y) -&gt; ++x 是一种误导性的方式来表述(x, y) -&gt; x+1,同样(x, y) -&gt; --x 实际上是(x, y) -&gt; x-1
  • 所以这个问题实际上只是在问 (1) 如何使用 List 和 (2) 如何使用给定元素实例化 ArrayList...
  • @Holger 即使 IDE 也应该在那里触发警告(如果 OP 正在使用)

标签: lambda java-8 containers lookup-tables binary-operators


【解决方案1】:

您当然可以创建这样的列表:

List<BinaryOperator<Integer>> opcodes = Arrays.asList((x, y) -> y, (x, y) -> ++x);

// sample
int a=14,b=16;
System.out.println(opcodes.get(0).apply(a, b)); // prints 16
System.out.println(opcodes.get(1).apply(a, b)); // prints 15

或纠正您尝试初始化列表的方式

List<BinaryOperator<Integer>> opcodes = new ArrayList<BinaryOperator<Integer>>() {{
    add((x, y) -> y);
    add((x, y) -> ++x);
    add((x, y) -> --x);
    add((x, y) -> x + y);
    add((x, y) -> x - y);
}};

【讨论】:

  • 您编写操作码列表的子类方式每次使用该方法时都会产生另一个子类,这确实效率低下
  • @Ferrybig 你的意思是Arrays.asList 初始化吗?没带你到那里。
  • 谢谢,完全忘记了 Arrays.asList()。我有建议将助记符更改为清除数字,因为这种方式更现实。好吧,助记符移到评论了:>
  • 不,所谓的双括号初始化实际上是创建ArrayList 的匿名子类,然后向其添加实例初始化程序。这是将项目添加到List 的一种非常低效的方式。 Arrays.asList 明显更好。
  • @Eugene 同意。然后enum 更惯用。只是您查询的措辞有点令人困惑。
【解决方案2】:

在额外的@nullpointer 的好答案中,您还可以考虑使用Map 键来保留函数的原始OPCODE 意图,这将是数组中的第一个,例如使用 Enum 作为键:

public enum OPCODES {
    MOV, ADD, XOR
}

哪些可以自举:

Map<OPCODES, BinaryOperator<Integer>> opcodeMap = 
  new EnumMap<OPCODES, BinaryOperator<Integer>>(OPCODES.class);
opcodeMap.put(OPCODES.ADD, (x, y)-> x + y);
opcodeMap.put(OPCODES.MOV, (x, y) -> y);
opcodeMap.put(OPCODES.XOR, (x, y) -> x ^ y);

并使用:

System.out.println(opcodeMap.get(OPCODES.ADD).apply(1, 2));
System.out.println(opcodeMap.get(OPCODES.MOV).apply(1, 2));
System.out.println(opcodeMap.get(OPCODES.XOR).apply(1, 2));

【讨论】:

  • 我的意思是运算符的意图将被数组“丢失”,除非为 MOV、INC 等的序数索引 0、1、2 定义了常量
【解决方案3】:

可以将 lambdas 存储在容器中,但真正的问题是您为什么要这样做?将它们存储在 List 中很容易,例如 Set/Map 怎么样 - 你不能为 lambdas 覆盖 equals/hashcode - 所以你不知道会发生什么。

既然你已经有一个Enum,为什么不使用更简单的方法:

Set<OPCODE> set = EnumSet.allOf(OPCODE.class);

【讨论】:

    【解决方案4】:

    因此,一旦您可以执行以下操作,您就已经定义了您的运算符:

    List<BinaryOperator<Integer>> opcodes = new ArrayList<BinaryOperator<Integer>>() {{
        add(OPCODE.ADD);
        add(OPCODE.DEC);
    }};
    

    在你的主要方法中测试:

    opcodes.forEach(elm -> System.out.println(elm.apply(1,2)));
    

    【讨论】:

    • 嗯,我相信这个想法是改变它而不是重复使用。但我会说仍然有效。尽管现在通过这种方法,任何一个 DS 都没有多大用处。
    • 你可以减少而不是 forEeching 来达到这个结果@nullpointer
    【解决方案5】:

    是的,您可以将 lambdas 放入列表或映射的值中。请记住,lambda 只是编写匿名类的一种奇特方式,而后者又只是 new 运算符的一个特例。换句话说,operators.add((x, y) -&gt; x + y) 只是

    的简写
    final BinaryOperator<Integer> ADD = new BinaryOperator<Integer>() {
        @Override
        public Integer apply(final Integer x, final Integer y) {
            return x + y;
        }
    };
    operators.add(ADD);
    

    按照同样的逻辑,operatorMap.put("add", (x, y) -&gt; x + y); 也会完全按照您的预期进行。

    但是,将 lambdas 放入一个集合(包括将它们用作映射键)可能不会达到您的预期。通常,集合的行为取决于 equalshashCode 的元素类型的定义,并且该语言不对这些方法做出任何保证,超出了 Object 定义的要求。因此,以下断言可能会失败:

    final Function<Object, String> func1 = Object::toString;
    final Function<Object, String> func2 = Object::toString;
    assert func1.equals(func2);
    

    以下类似:

    final Function<Object, String> func = Object::toString;
    final Set<Object> set = new HashSet<>();
    set.add(func);
    assert set.contains(Object::toString);
    

    因此,将 lambdas 放入基于 Set 的容器时要小心,包括用作 Map 键,但它们可以放入 Lists 并用作 Map 值就好了。

    【讨论】:

      【解决方案6】:

      完善@naomimyselfandi 的答案。

      你可以经常Function 使用一些咖喱魔法:

      List<Function<Integer, Function<Integer, Integer>>> opcodes = Arrays.asList(
          (x -> y -> y),
          (x -> y -> ++x),
          (x -> y -> --x),
          (x -> y -> x + y),
          (x -> y -> x - y)
      );
      

      看 @Holger 评论: Can a java lambda have more than 1 parameter?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-12
        • 2015-10-19
        • 1970-01-01
        • 1970-01-01
        • 2017-11-16
        • 1970-01-01
        • 2018-02-04
        相关资源
        最近更新 更多