web-dev-qa-db-ja.com

Java 8-ラムダをリストに格納する

たとえば、ラムダをいくつかのコンテナに格納することは可能かどうか疑問に思います。 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);

それも可能ですか?

16
Jump3r

あなたは確かに次のようなリストを作成することができます:

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);
}};
12
Naman

追加の@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));
9
StuartLC

あなたcouldラムダをコンテナ内に保存しますが、本当の問題はなぜそれをしたいのかということです。それらをListに格納するのは簡単です。たとえば、Set/Mapはどうでしょうか。ラムダの場合はequals/hashcodeをオーバーライドできないため、何が起こるかわかりません。

すでにEnumがあるので、もっと簡単な方法を使ってみませんか。

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

したがって、次のようなことができるようになったら、演算子を定義しました。

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

あなたのメインメソッドでそれをテストするには:

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

はい、マップのリストまたは値にラムダをうまく配置できます。ラムダは匿名クラスを作成するための優れた方法であり、これはnew演算子の特殊なケースにすぎないことを忘れないでください。言い換えれば、operators.add((x, y) -> 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) -> x + y);も期待どおりの動作をします。

ただし、ラムダをセットに入れると(マップキーとして使用することも含まれます)、期待どおりに動作しない可能性があります。一般に、セットの動作は、要素タイプによるequalsおよびhashCodeの定義に依存し、言語は、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);
_

したがって、ラムダをSetベースのコンテナに入れる場合は注意が必要です。これには、Mapキーとしての使用も含まれますが、Listsに入れて、Map値として使用することもできます。

0
naomimyselfandi