web-dev-qa-db-ja.com

Java-オブジェクトのリストをプロパティ属性の値を持つリストにマッピングする

次のように定義されたViewValueクラスがあります。

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;

}

この問題に対するより良いアプローチはありますか?

33
mgamer

編集:この答えは、コードの他の場所で異なるエンティティと異なるプロパティに対して同様のことを行う必要があるという考えに基づいています。 のみ ViewValuesのリストをIDでLongsのリストに変換する必要がある場合は、元のコードを使用してください。ただし、より再利用可能なソリューションが必要な場合は、続きを読んでください...

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

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);

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

率直に言って、これはすべてラムダ式の方がはるかに優れていますが、気にしないでください...

27
Jon Skeet

Commons BeanUtilsとCollectionsを使用して、1行でそれを行うことができます。
(他の人があなたのためにそれをやったとき、なぜあなた自身のコードを書くのですか?)

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

...

List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues, 
                                       new BeanToPropertyValueTransformer("id"));
33
Ulf Lindback

Java 8を使用して、1行のコードで実行できます。

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

詳細:Java 8-Streams

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:

On Java 8 Guavaは必要ありません。次のことができます。

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());

次の更新(JavaslangからVavrへの名前変更後の最後の更新):

現在、ソリューションについて言及する価値があります Javaslangライブラリ( http://www.javaslang.io/ Vavrライブラリ( http://www.vavr.io/ )。本物のオブジェクトのリストがあると仮定しましょう:

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

JavaslangライブラリのListクラスを使用して変換を行うことができます(長期的には収集は便利ではありません)。

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

ただし、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);

私は、そのライブラリで利用可能なコレクションと新しい型を見てみることをお勧めします(特にTry型が好きです)。ドキュメントは次のアドレスにあります。 http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/

PS。 Oracleおよび名前内の「Java」Wordにより、ライブラリ名をjavaslangから別の名前に変更する必要がありました。彼らはVavrに決めました。

29
Przemek Nowak

このユースケース用の小さな機能ライブラリを実装しました。メソッドの1つにこのシグネチャがあります。

<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType)

これは文字列を取得し、リフレクションを使用してプロパティの呼び出しを作成し、このプロパティ呼び出しを使用してgetおよびiteratorが実装されたobjectListを基にしたリストを返します。

MapToProperty関数は、別の投稿で説明したように、関数をマッパーとして使用する一般的なマップ関数の観点から実装されています。とても便利です。

基本的な関数プログラミングを読んで、特にFunctor(マップ関数を実装するオブジェクト)に目を通すことをお勧めします

編集:反射は本当に高価である必要はありません。 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));
    }
}
4
John Nilsson

ラッパーを作成できます:

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
}

それはさらに退屈な作業ですが、パフォーマンスが向上する可能性があります。

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

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

3

それはあなたが_List<Long>_と_List<ViewValue>_で何をするかに依存します

たとえば、_List<ViewValue>_をラップする独自のList実装を作成し、ViewValuesを反復処理するイテレータ実装でiterator()を実装し、IDを返すことで十分な機能を取得できます。

2
Stephen Denne

次のように、オブジェクトのリストのプロパティ(キーとしてid、値としていくつかのプロパティなど)からマップを作成できます。

Map<String, Integer> mapCount = list.stream().collect(Collectors.toMap(Object::get_id, Object::proprty));
0
Pravin