2009-04-10 11 views
24

属性は次のように私はViewValueクラスが定義されています:のJava - そのプロパティの値をリストにオブジェクトのリストをマップには

どこか私のコードで
class ViewValue { 

private Long id; 
private Integer value; 
private String description; 
private View view; 
private Double defaultFeeRate; 

// getters and setters for all properties 
} 

私はViewValueインスタンスのリストを変換する必要があります対応するViewValueからのidフィールドの値を含むリスト。

私はそれを使用してくださいforeachループ:

List<Long> toIdsList(List<ViewValue> viewValues) { 

    List<Long> ids = new ArrayList<Long>(); 

    for (ViewValue viewValue : viewValues) { 
     ids.add(viewValue.getId()); 
    } 

    return ids; 

}

はこの問題へのよりよい方法はありますか?

答えて

22

EDIT:この回答は、コード内の別のエンティティと異なるプロパティに対して同様の処理を行う必要があるという考え方に基づいています。 のみViewValuesのリストをIDでLongのリストに変換する必要がある場合は、元のコードを使用してください。しかし、より再利用可能なソリューションが必要な場合は、次の記事をお読みください。

私は投影用のインターフェースを宣言します。

public interface Function<Arg,Result> 
{ 
    public Result apply(Arg arg); 
} 

その後、あなたは、単一の汎用的な変換方法書くことができます。そして、あなたはこのような単純な予測を定義することができます

public <Source, Result> List<Result> convertAll(List<Source> source, 
    Function<Source, Result> projection) 
{ 
    ArrayList<Result> results = new ArrayList<Result>(); 
    for (Source element : source) 
    { 
     results.add(projection.apply(element)); 
    } 
    return results; 
} 

を:

private static final Function<ViewValue, Long> ID_PROJECTION = 
    new Function<ViewValue, Long>() 
    { 
     public Long apply(ViewValue x) 
     { 
      return x.getId(); 
     } 
    }; 

をそして、ちょうどこのようにそれを適用します。

List<Long> ids = convertAll(values, ID_PROJECTION); 

(Obvi ously K & Rブレースと長い行を使用すると、投影宣言が少し短くなります:)

率直に言って、このすべてのは、ラムダ式とたくさんよりよいが、気にしないだろう...

+4

非常Javaesqueソリューション - はなく、良い方法インチそれは正確にコードの量や複雑さを減らすことはありませんか? –

+1

私は個人的にJava-esqueよりもLINQ-esqueの方がいいと思います。私はむしろ変換コードを作成してから投影アスペクトを分離したいと思いますが、変換ロジックの負荷が重複している場合は問題ありません。もちろん、ラムダ式ではもっとうまくいくでしょう。 –

+1

JavaのComparatorと同じようなことはありませんか? –

2

何に依存していることその後、例えばList<Long>、およびList<ViewValue>

で行うあなたはViewValuesを反復は、IDを返すことをイテレータの実装にiterator()を実施し、List<ViewValue>をラップ独自のリストの実装を作成するのに十分な機能を得る可能性があります。

3

あなたはラッパーをUDEできます

public class IdList impements List<Long> 
{ 
    private List<ViewValue> underlying; 

    pubic IdList(List<ViewValue> underying) 
    { 
     this.underlying = underying; 
    } 

    public Long get(int index) 
    { 
     return underlying.get(index).getId() 
    } 

    // other List methods 
} 

ザッツさらに退屈な仕事けれども、それはパフォーマンスを向上させることができます。

リフレクションを使用してあなたと私のソリューションを一般的に実装することもできますが、それはパーフォンスにとって非常に悪いことです。

TheresはJavaで短くて簡単な汎用ソリューションであり、Imは恐れています。 Groovyでは、単にcollect()を使用しますが、リフレクションも含まれていると思います。

3

私はこのユースケース用の小さな関数ライブラリを実装しました。

文字列を取得し、プロパティの呼び出しを作成するためにリフレクションを使用しています
<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType) 

は、それが取得し、イテレータがこのプロパティの呼び出しを使用して実装objectListに裏打ちされたリストを返します:方法の一つは、この署名を持っています。

mapToProperty関数は、説明されている別の記事と同様に、関数をマッパーとしてとる一般的なマップ関数の形で実装されています。非常に便利です。

私はあなたが基本的なfunctionlプログラミングをよく読んで、特にファンクタ(マップ機能を実装しているオブジェクト)

編集を見てみましょうお勧め:リフレクションは本当に高価である必要はありません。この分野ではJVMが大幅に改善されています。呼び出しを一度コンパイルして再利用してください。

EDIT2:サンプルコード

public class MapExample { 
    public static interface Function<A,R> 
    { 
     public R apply(A b); 
    } 

    public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> propertyType) 
    { 
     try { 
      final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1)); 

      if(!propertyType.isAssignableFrom(m.getReturnType())) 
       throw new IllegalArgumentException(
        "Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName() 
       ); 

      return new Function<A,R>() 
      { 
       @SuppressWarnings("unchecked") 
       public R apply(A b) 
       { 
        try { 
         return (R)m.invoke(b); 
        } catch (Exception e) { 
         throw new RuntimeException(e); 
        } 
       }; 
      }; 

     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public static <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper) 
    { 
     return new AbstractList<T2>() 
     { 
      @Override 
      public T2 get(int index) { 
       return mapper.apply(list.get(index)); 
      } 

      @Override 
      public int size() { 
       return list.size(); 
      } 
     }; 
    } 

    @SuppressWarnings("unchecked") 
    public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType) 
    { 
     if(list == null) 
      return null; 
     else if(list.isEmpty()) 
      return Collections.emptyList(); 

     return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType)); 
    } 
} 
31

あなたはコモンズ々BeanUtilsやコレクションを使用してワンライナーでそれを行うことができます:
(他の人があなたのためにそれを行っているとき、なぜ独自のコードを書くのか?)

import org.apache.commons.beanutils.BeanToPropertyValueTransformer; 
import org.apache.commons.collections.CollectionUtils; 

... 

List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues, 
             new BeanToPropertyValueTransformer("id")); 
+3

+1のコメント "他の人があなたのためにしたときに自分のコードを書く理由は?"これは、私たちが日常的に直面している問題です。すでに解決されている問題の解決策を作成する必要があります。 –

+0

PropertyUtils.getProperty(object、propertyName);フードの下で反射を使用します。これは余分なパフォーマンスコストを伴います。そのためには、私はJon Skeetのアプローチを使用します。 –

+4

私がこれについて気に入らないのは、型安全性の欠如です。「viewValue.id」フィールドの名前が変更されても、コードはコンパイルされますが実行時には失敗します。 –

24

Googleのコレクションを使用してください。例:

Function<ViewValue, Long> transform = new Function<ViewValue, Long>() { 
     @Override 
     public Long apply(ViewValue from) { 
      return from.getId(); 
     } 
    }; 
    List<ViewValue> list = Lists.newArrayList(); 
    List<Long> idsList = Lists.transform(list, transform); 

UPDATE:

は、Java 8であなたがグアバは必要ありません。次のことができます。

import com.example.ViewValue; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.function.Function; 
import java.util.stream.Collectors; 

Function<ViewValue, Long> transform = ViewValue::getId; 
List<ViewValue> source = new ArrayList<>(); 
List<Long> result = source.stream().map(transform).collect(Collectors.toList()); 

それとも:

List<ViewValue> source= new ArrayList<>(); 
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList()); 

次の更新(Vavr名の変更にJavaslang後の最後の1):

現在のところ、それは と解決策について言及する価値がありますJavaslangライブラリ( http://www.javaslang.io/ Vavrライブラリ(http://www.vavr.io/)。

List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList(); 

しかし、あなたは以下となります。私たちは(長い収集が便利ではない実行する上)JavaslangライブラリからListクラスで変換を行うことができ

List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2)); 

:我々は本物のオブジェクトと私たちのリストを持っていると仮定しましょうのみJavaslangリストで電源を参照してください。

io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3)); 
io.vavr.collection.List<Long> res = source.map(ViewValue::getId); 

私は(私は特に試しタイプが好き)、そのライブラリのコレクションと新しいタイプの利用可能な外観を取るように促します。次のアドレスにあるドキュメントをご覧ください。 http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/

PS。 Oracleと名前の中の "Java"という単語のために、彼らはjavaslangから別のものにライブラリ名を変更しなければならなかった。彼らはVavrに決めました。

14

我々はより多くの情報のためのJava 8

List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList()); 

を使用して単一行のコードでそれを行うことができます。Java 8 - Streams

+0

幸いなことに私たちは開発中のものを単純化しています:) –

+2

私はそれをまっすぐにしておきます:コメントを追加します。これは、書き込み時に利用できなかったJava8で有効になります。答えは、実際には7年前に書かれたものへの大きな一歩です。 – Alex

関連する問題