Pertanyaan Java - memetakan daftar objek ke daftar dengan nilai atribut propertinya


Saya memiliki kelas ViewValue yang didefinisikan sebagai berikut:

class ViewValue {

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

// getters and setters for all properties
}

Di suatu tempat di kode saya saya perlu mengonversi daftar instance ViewValue ke daftar yang berisi nilai bidang id dari ViewValue yang sesuai.

Saya melakukannya menggunakan foreach loop:

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

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

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

   return ids;

}

Apakah ada pendekatan yang lebih baik untuk masalah ini?


27
2018-04-10 10:22


asal


Jawaban:


EDIT: Jawaban ini didasarkan pada gagasan bahwa Anda harus melakukan hal yang serupa untuk entitas yang berbeda dan properti yang berbeda di tempat lain dalam kode Anda. Jika kamu hanya perlu mengkonversi daftar ViewValues ​​ke daftar Longs oleh ID, kemudian tetap dengan kode asli Anda. Jika Anda menginginkan solusi yang lebih dapat digunakan kembali, baca terus ...

Saya akan menyatakan antarmuka untuk proyeksi, mis.

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

Kemudian Anda dapat menulis satu metode konversi umum:

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

Kemudian Anda dapat menentukan proyeksi sederhana seperti ini:

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

Dan menerapkannya seperti ini:

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

(Jelas menggunakan K & R menguatkan dan garis yang lebih panjang membuat deklarasi proyeksi sedikit lebih pendek :)

Terus terang semua ini akan jauh lebih baik dengan ekspresi lambda, tetapi tidak masalah ...


24
2018-04-10 10:30



Anda dapat melakukannya dalam satu baris menggunakan Commons BeanUtils and Collections:
(mengapa menulis kode Anda sendiri ketika orang lain melakukannya untuk Anda?)

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

...

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

32
2017-10-30 08:52



Gunakan koleksi google. Contoh:

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

MEMPERBARUI:

Di Jawa 8 Anda tidak perlu Guava. Kamu bisa:

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

Atau hanya:

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

NEXT UPDATE (Yang terakhir setelah Javaslang berubah nama Vavr):

Saat ini ada baiknya untuk menyebutkan tentang solusi dengan Javaslang library (http://www.javaslang.io/) Pustaka Vavr (http://www.vavr.io/). Anggaplah kita memiliki daftar kami dengan objek asli:

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

Kita bisa melakukan transformasi dengan kelas List dari pustaka Javaslang (dalam jangka panjang pengumpulannya tidak mudah):

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

Tetapi Anda akan melihat kekuatan hanya dengan daftar 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);

Saya mendorong untuk melihat koleksi yang tersedia dan tipe baru di perpustakaan itu (saya suka terutama tipe Coba). Anda akan menemukan dokumentasi di bawah alamat berikut: http://www.javaslang.io/javaslang-docs/  http://www.vavr.io/vavr-docs/.

PS. Karena Oracle dan kata "Java" dalam nama mereka harus mengubah nama perpustakaan dari javaslang menjadi sesuatu yang lain. Mereka telah memutuskan untuk Vavr.


28
2018-02-16 08:36



Kita bisa melakukannya dalam satu baris kode menggunakan java 8

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

Untuk info lebih lanjut: Java 8 - Streaming


21
2018-06-10 09:18



Saya telah mengimplementasikan perpustakaan fungsional kecil untuk usecase ini. Salah satu metode memiliki tanda tangan ini:

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

Yang mengambil string dan menggunakan refleksi untuk membuat panggilan ke properti kemudian mengembalikan Daftar yang didukung oleh objectList di mana get dan iterator diimplementasikan menggunakan panggilan properti ini.

Fungsi mapToProperty diimplementasikan dalam bentuk fungsi peta umum yang mengambil Fungsi sebagai mapper, sama seperti posting lain yang dijelaskan. Sangat berguna.

Saya sarankan Anda membaca tentang pemrograman functionl dasar dan khususnya lihatlah Functors (objek yang mengimplementasikan fungsi peta)

Edit: Refleksi memang tidak harus mahal. JVM telah berkembang pesat di bidang ini. Pastikan untuk mengkompilasi permohonan sekali dan menggunakannya kembali.

Edit2: Contoh kode

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
2018-04-11 15:09



Anda bisa memilih pembungkus:

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
}

Meskipun itu bahkan pekerjaan yang lebih membosankan, itu bisa meningkatkan kinerja.

Anda juga dapat menerapkan solusi Anda dan generik saya menggunakan refleksi, tetapi itu sangat buruk untuk perforasi.

Tidak ada solusi generik yang singkat dan mudah di Jawa, saya takut. Di Groovy, Anda cukup menggunakan collect (), tetapi saya yakin itu juga melibatkan refleksi.


3
2018-04-10 10:33



Itu tergantung pada apa yang kemudian Anda lakukan dengan List<Long>, dan List<ViewValue>

Misalnya Anda mungkin mendapatkan fungsionalitas yang cukup dari membuat implementasi Daftar Anda sendiri yang membungkus a List<ViewValue>, menerapkan iterator() dengan implementasi iterator yang iterasi atas ViewValues, mengembalikan id.


2
2018-04-10 10:31