2009-05-22 15 views
47

Javaはパラメータとしてメソッドを渡すことができないので、Javaでリストの理解のようにPythonを実装するのにどのようなトリックを使用しますか?JavaでのPythonのようなリストの理解

私は文字列のリスト(ArrayList)を持っています。私は別のリストを得るために関数を使って各要素を変換する必要があります。私は文字列を入力として受け取り、別の文字列を出力として返すいくつかの関数を持っています。リストと関数をパラメータとして与えることができる汎用メソッドを作成するにはどうすればよいのでしょうか。文字通りの意味では不可能ですが、私はどんなトリックを使うべきですか?

もう一つの選択肢は、リスト全体を単にループする小さな文字列処理関数ごとに新しい関数を書くことです。これはあまりクールではありません。

+2

をfyiとして使用する場合は、JythonまたはScalaを使用して、JVM – geowa4

+3

...またはClojure!のリスト内包表記を取得できます。 :) – Ashe

+0

これに関するすべての答えを読みながらSMH。 Pythonでは、40〜60文字の1行にリスト内包表記を簡単に書くことができます。ここで提案されているソリューションはすべて複数の行で、そのほとんどはPythonで使用する単一の行よりも長くなっています。 – ArtOfWarfare

答えて

33

基本的に、あなたは関数インタフェースを作成:

public interface Func<In, Out> { 
    public Out apply(In in); 
} 

をして、あなたの方法に匿名のサブクラスに渡します。

あなたの方法は、インプレースの各要素に関数を適用することができ、次のいずれか

public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) { 
    ListIterator<T> itr = list.listIterator(); 
    while (itr.hasNext()) { 
     T output = f.apply(itr.next()); 
     itr.set(output); 
    } 
} 
// ... 
List<String> myList = ...; 
applyToListInPlace(myList, new Func<String, String>() { 
    public String apply(String in) { 
     return in.toLowerCase(); 
    } 
}); 

または(基本的には出力リストへの入力リストからマッピングを作成する)新しいListを作成します。

public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) { 
    List<Out> out = new ArrayList<Out>(in.size()); 
    for (In inObj : in) { 
     out.add(f.apply(inObj)); 
    } 
    return out; 
} 
// ... 
List<String> myList = ...; 
List<String> lowerCased = map(myList, new Func<String, String>() { 
    public String apply(String in) { 
     return in.toLowerCase(); 
    } 
}); 

どちらが適しているかは、ユースケースによって異なります。リストが非常に大きい場合は、インプレース解決策が唯一実行可能な解決策である可能性があります。多くの異なる機能を同じ元のリストに適用して多くの派生リストを作成する場合は、mapバージョンが必要です。

+1

しかし、標準の名前(あなたのケースでは 'apply')を持たなければならないので、小さな関数をすべて別のクラスに入れるように求めています。右 ? – euphoria83

+1

必ずしもそうではありません。あなたの匿名クラスはapply()内の小さな関数を単に呼び出すことができます。これは、Javaがリフレクションの危険を冒すことなくポインタを機能させるのと同じくらい近いです。 –

+0

doToListはホイールを再改造しています。あなたがここでやったことは、通常マップと呼ばれるものの貧弱なデザインです。通常のインターフェイスはpublic static リスト map(リスト、Func f)です。それが何をするかは、代わりに別のリストを生成することです。参照を破棄せずに元のリストを変更する必要がある場合は、単に.clear()の後にaddAll()を実行してください。 1つの方法ですべてを組み合わせないでください。 – Pyrolistical

16

Google Collections libraryは、プレーンなJavaサポートよりもはるかに高いレベルで、機能的な方法(フィルタ、マップ、フォールドなど)で、コレクションとイテレータを扱うためのクラスがたくさんあります。 FunctionとPredicateのインタフェースとメソッドを定義し、コレクションを処理して、そうする必要がないようにします。また、Javaジェネリックの扱いを難しくする便利な機能も備えています。

また、フィルタリングコレクションにHamcrest **を使用します。

2つのライブラリは、アダプタクラスとの組み合わせが容易です。


**関心の宣言:私は共同書いたHamcrest

+11

好奇心に欠けて、なぜそれはハムクレストと呼ばれていますか?私はまだそれがおいしいかどうかわからない。 –

+12

「マッチャー」のアナグラムです。 – Nat

5

Apache Commons CollectionsUtil.transform(Collection, Transformer)は別のオプションです。

+0

残念ながら、それは一般的ではありません。この場合、それは何らかの余分なキャスティングですが、他のケースでは問題になる可能性があります。 –

27

のJava 8で使用できるメソッド参照:

List<String> list = ...; 
list.replaceAll(String::toUpperCase); 

それとも、新しいリストのインスタンスを作成する場合:

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList()); 
+7

質問は7歳で、Java 8は存在しませんでした。これは現在受け入れられている回答でなければなりません;) – zpontikas

1

私はリスト内包を書くために、このプロジェクトを構築していますがJavaは、今https://github.com/farolfo/list-comprehension-in-java

例におけるコンセプトの証明である

// { x | x E {1,2,3,4}^x is even } 
// gives {2,4} 

Predicate<Integer> even = x -> x % 2 == 0; 

List<Integer> evens = new ListComprehension<Integer>() 
    .suchThat(x -> { 
     x.belongsTo(Arrays.asList(1, 2, 3, 4)); 
     x.is(even); 
    }); 
// evens = {2,4}; 

そして、我々は

// { x * 2 | x E {1,2,3,4}^x is even } 
// gives {4,8} 

List<Integer> duplicated = new ListComprehension<Integer>() 
    .giveMeAll((Integer x) -> x * 2) 
    .suchThat(x -> { 
     x.belongsTo(Arrays.asList(1, 2, 3, 4)); 
     x.is(even); 
    }); 
// duplicated = {4,8} 
+1

Pythonリストの理解の美しさの一部は、どれほど短いかです。あなたの6つの長いJavaの行は、 '' x%1、2、3、4ならばx%2 == 0なら '' x * 2 ''... 1行41文字のように書くことができます。あなたのライブラリが何かを簡潔にしてくれていないため、どのくらいのコードがどれほど大丈夫かというだけで、あなたのコードのどれくらい読んでも大したことはありません。 – ArtOfWarfare

+0

これは他の多くのソリューションよりも優れていますが、実際にはこれが好きです – rhbvkleef

0

ようないくつかの方法で出力式を変換したい場合は、そのような機能のためのラムダを使用することができます。

class Comprehension<T> { 
    /** 
    *in: List int 
    *func: Function to do to each entry 
    */ 
    public List<T> comp(List<T> in, Function<T, T> func) { 
     List<T> out = new ArrayList<T>(); 
     for(T o: in) { 
      out.add(func.apply(o)); 
     } 
     return out; 
    } 
} 

使用:

List<String> stuff = new ArrayList<String>(); 
stuff.add("a"); 
stuff.add("b"); 
stuff.add("c"); 
stuff.add("d"); 
stuff.add("cheese"); 
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String> 
    a.equals("a")? "1": 
      (a.equals("b")? "2": 
       (a.equals("c")? "3": 
        (a.equals("d")? "4": a 
    ))) 
}); 

が返されます。

["1", "2", "3", "4", "cheese"] 
関連する問題