2016-12-12 25 views
0

私に整数を与えるアルゴリズムがあります。 このIntegerに基づいて、メソッドを呼び出したいと思います。 すべての整数は一意であり(データベース内の主キーのような)、呼び出す方法は1つあります。すべてのメソッドは同じデータ型を返します。メソッドはすべて同じクラスにあり、このクラスで呼び出されます。キーで特定のメソッドを呼び出す最も効率的な方法|| refactorメソッド呼び出しswitch文

検索の時間がたってから、私はこの2つのソリューションしか得られませんが、どちらが「より良い」か分かりません。 (運転時間、リソース)

スイッチソリューション: 最初のアイデアではなく

switch (code) { 
    case 1: 
     nextOperation = doMethod1(); 
     break; 

    case 2: 
     nextOperation = doMethod2(); 
     break; 

    //many more cases... 

    default: 
     break; 
    } 

public MyObject doMethod1(MyObject myObject){ 
    //do something with operation 
    return myObject; 
    } 

反射ソリューションは非常に良い感じている:多分悪い走行時間

try{ 
     String methodName = "doMethod" + Integer.toString(operation.getOperationCode()); 
     //operation.getOperationCode() same like code in switch solution 
     Method method = this.class.getDeclaredMethod(methodName, parametertype); 
     nextOperation = (MyObject) method.invoke(this, parameter); 
    } 
    catch (Exception e){ 
     LogReport.writeLog(e.toString()); //own Log-Report filewriter 
    } 

は多分あります(?)私の問題や他の解決策の良い方法? 私に少しヒントを与えることができれば、私はとてもうれしいでしょう。

+2

他の大きな問題は、静的解析を傷つけることです。 'doMethod1'が呼び出される場所をIDEに表示するように頼むことはできません。 – yshavit

+1

これらの2つの方法は異なります。最初のメソッドでは 'this 'のメソッドを呼び出し、2番目のメソッドでは' nextOperation'でメソッドを呼び出します。第2のものも 'nextOperation'の値を変更しません。 – Bubletan

+1

コードは連続していますか?次に、 'MethodHandle'sまたはメソッドを呼び出すいくつかの関数の配列(場合によっては変更不可能なリスト)を使うことができます。 – Bubletan

答えて

5

第三の選択肢は、数字からRunnablesにマップを構築して呼び出すメソッドを調べることであろう。どのようにランニングタイムを比較するのか分かりませんが、リフレクションを使うよりも速くなると思います。私が使用している

Map<Integer, Runnable> methodMap = new ConcurrentHashMap<>(); 
methodMap.put(1,() -> doMethod1()); 
methodMap.put(2,() -> doMethod2()); 
methodMap.put(3,() -> doMethod3()); 
// ... and so on ... 

// look up the method and run it: 
Runnable method = methodMap.get(code); 
if (method != null) { 
    method.run(); 
} 

このマップはあなたのプログラムの実行中にその場で変更する必要がありますが、念のためにConcurrentHashMapあなたは最初に一度だけマップを構築し、その後、平野、それを変更しない場合HashMapも同様です。私はラムダを使ってそれぞれのメソッドを呼び出すためにRunnablesを作成しました。

マップを作成するコードはまだかなり長くなります。マップを作成するためにリフレクションを使用することでそれを短縮することができます。それは遅くなりますが、地図を作成したときには、数値でメソッドをディスパッチする必要はなく、リフレクションのペナルティを1回だけ支払うことになります。

注:ここではラムダを使用しなくてもマップにメソッドを追加する別の方法です:

methodMap.put(1, new Runnable() { public void run() { doMethod1(); } }); 
methodMap.put(2, new Runnable() { public void run() { doMethod2(); } }); 
methodMap.put(3, new Runnable() { public void run() { doMethod3(); } }); 
// etc. 

それは匿名内部クラスで行われるだろうか。すなわち、ラムダは本質的に匿名のメソッドであり、()を引数とし、->の後の式は呼び出すコードです(doMethod1()など)。コンパイラはこれがMap<Integer, Runnable>putメソッドに渡されているとみなし、Runnablerunメソッドに匿名メソッドをとり、そのコードを使用してRunnableを作成します。

+0

スイッチが適用可能な代替手段である場合、実行時に地図が変更されることはほとんどありません。 – Bubletan

+0

マップが実行時に変更されるのはなぜですか? – Phil

+1

@Phil私はそれがそれを知りません。残りのコードが何をしているのかわからないので、私はちょっと注意していました。それを行う最善の方法は、地図を一度作成してからそれを変更することはなく、地図を読み込むだけです。 –

3

あなたは使用することができます、その後

Map<Integer, Supplier<Operation>> map = new ...; 
map.put(1,() -> doMethod1()); 
map.put(2,() -> doMethod2()); 

およびコール:

nextOperation = map.get(operationCode).get(); 
+0

スタイルノート: '() - > doMethod1'または' MyClass :: doMethod1'のいずれかがうまくいくと思います。 – yshavit

+2

@yshavit 'MyClass :: doMethod1'は、メソッドが静的である場合にのみ機能します。 – shmosel

+1

'this :: doMethod1'についてはどうですか? –

1

Java 8を使用している場合は、lambdaを使用してメソッドを呼び出して、整数HashMapにマッピングすることができます。

インタフェース:

public interface MyInterface { 
    void excecute(); 
} 

を初期化方法:

private int i = 0; 
... 
... 
HashMap<Integer, MyInterface> myMap = new HashMap<>(); 
myMap.put(0,() -> { 
    i = doMethod0(); 
}); 

呼び出す方法:

1
myMap.get(i).excecute(); 

これを行う別の方法(Java 7と互換性がありますが、Androidでサポートされているかどうかわかりませんが)はMethodHandlesを使用します。マップを作成するためのコードをすべて記述する必要はありません(コードを生成するためのスクリプトを記述することは可能ですが、おそらく一度だけ実行する必要があるため)。ルックアップが実行されるときにアクセスチェックが事前に実行されるため、リフレクションより高速です。これは、メソッドがしているクラスがMyClass命名されていることを、方法はstaticであることを前提とした方法が問題のようMyObjectを取り、返すことが、問題はその上で一貫していないことを

import java.lang.invoke.MethodHandle; 
import java.lang.invoke.MethodHandles; 
import java.lang.invoke.MethodType; 
import java.util.HashMap; 
import java.util.Map; 

Map<Integer, MethodHandle> handleByNumber = new HashMap<>(); 
MethodHandles.Lookup = MethodHandles.lookup(); 
MethodType mt = MethodType.methodType(MyObject.class, MyObject.class); 

int number = 1; // find all doMethodN methods from 1 up to whatever 
while (true) { 
    try { 
     MethodHandle mh = lookup.findStatic(MyClass.class, "doMethod" + number, mt); 
     handles.put(number, mh); 
     number++; 
    } catch (NoSuchMethodException | IllegalAccessException e) { 
     break; 
    } 
} 

注意。これらは変更する必要があります。

メソッドが静的でない場合は、lookup.findStaticの代わりにlookup.findVirtualが使用されます。メソッドが静的でない場合は、受信機(あなたがメソッドの呼び出しているオブジェクト)で提供される必要がある

MethodHandle mh = handles.get(code); 
if (mh != null) { 
    try { 
     MyObject result = (MyObject) mh.invokeExact(obj); 
    } catch (Throwable e) { 
     throw new RuntimeException(e); // or other handling as appropriate 
    } 
} 

objがパラメータである数でメソッドを呼び出すために

電話:

MyObject result = (MyObject) mh.invokeExact(receiver, obj); 
+0

この他の良い解決策と実行時の改善に感謝します。 (前に 'MethodHandle'を聞いたことはありませんでした)しかし、mh.invokeExact()で直接メソッドを取得するとマップを使うべきですか?そして、私はAndroidで 'MethodHandle'を使うことはできないようですが、' java.lang.invoke'の穴がうまくいかなかったようです... – Phil

+1

@Phil地図に保存する代わりに毎回それを見ることができると思います。それがパフォーマンスにどのように影響するかはわかりません。とにかく、あなたはそれを使用することができないように聞こえる。 Runnablesのあるものは、私のラップトップのいくつかのテストでは、MethodHandlesとほぼ同じ速度でした。 –