Apa cara terbaik untuk memfilter Java Collection?


Jawaban:


699

Java 8 ( 2014 ) memecahkan masalah ini menggunakan stream dan lambdas dalam satu baris kode:

List<Person> beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16).collect(Collectors.toList());

Ini tutorialnya .

Gunakan Collection#removeIfuntuk memodifikasi koleksi di tempat. (Perhatikan: Dalam hal ini, predikat akan menghapus objek yang memenuhi predikat):

persons.removeIf(p -> p.getAge() <= 16);

lambdaj memungkinkan penyaringan koleksi tanpa menulis loop atau kelas dalam:

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));

Bisakah Anda membayangkan sesuatu yang lebih mudah dibaca?

Penafian: Saya seorang kontributor pada lambdaj


34
Bagus tapi impor statis mengaburkan apa yang terjadi. Sebagai referensi, pilih / miliki / aktif adalah impor statis di ch.lambdaj.Lambda, lebih besar dari ini adalah org.hamcrest.Matchers
MikePatel

11
LambdaJ benar-benar seksi, tetapi perlu dicatat bahwa itu menyiratkan overhead yang signifikan (rata-rata 2,6): code.google.com/p/lambdaj/wiki/PerformanceAnalysis .
Doc Davluz


7
Sangat suka contoh LamdaJ ini ... mirip dengan fungsi .NET bawaan Lambda. Dan di mana seseorang dapat minum pada usia 16? Kami harus mempertimbangkan untuk menambahkan batasan lokalisasi. : P
MAbraham1

3
removeIf contoh seharusnyapersons.removeIf(p -> p.getAge() <= 16);
vim

223

Dengan asumsi bahwa Anda menggunakan Java 1.5 , dan bahwa Anda tidak dapat menambahkan Google Collections , saya akan melakukan sesuatu yang sangat mirip dengan yang dilakukan orang-orang Google. Ini sedikit variasi pada komentar Jon.

Pertama tambahkan antarmuka ini ke basis kode Anda.

public interface IPredicate<T> { boolean apply(T type); }

Para pelaksana dapat menjawab ketika predikat tertentu benar untuk jenis tertentu. Misalnya Jika Titu Userdan AuthorizedUserPredicate<User>mengimplementasikanIPredicate<T> , kemudian AuthorizedUserPredicate#applykembali apakah disahkan padaUser berwenang.

Kemudian di beberapa kelas utilitas, bisa dibilang

public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
    Collection<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

Jadi, dengan asumsi bahwa Anda memiliki penggunaan di atas mungkin

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        return user.isAuthorized();
    }
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);

Jika kinerja pada pemeriksaan linier menjadi perhatian, maka saya mungkin ingin memiliki objek domain yang memiliki koleksi target. Objek domain yang memiliki kumpulan target akan memiliki logika penyaringan untuk metode yang menginisialisasi, menambah dan mengatur koleksi target.

MEMPERBARUI:

Di kelas utilitas (katakanlah Predikat), saya telah menambahkan metode pilih dengan opsi untuk nilai default ketika predikat tidak mengembalikan nilai yang diharapkan, dan juga properti statis untuk params untuk digunakan di dalam IPredicate baru.

public class Predicate {
    public static Object predicateParams;

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element : target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
        T result = null;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
        T result = defaultValue;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }
}

Contoh berikut mencari benda yang hilang di antara koleksi:

List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
    new IPredicate<MyTypeA>() {
        public boolean apply(MyTypeA objectOfA) {
            Predicate.predicateParams = objectOfA.getName();
            return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                public boolean apply(MyTypeB objectOfB) {
                    return objectOfB.getName().equals(Predicate.predicateParams.toString());
                }
            }) == null;
        }
    });

Contoh berikut, mencari sebuah instance dalam koleksi, dan mengembalikan elemen pertama dari koleksi sebagai nilai default ketika instance tidak ditemukan:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

UPDATE (setelah rilis Java 8):

Sudah beberapa tahun sejak saya (Alan) pertama kali memposting jawaban ini, dan saya masih tidak percaya saya mengumpulkan poin SO untuk jawaban ini. Bagaimanapun, sekarang Java 8 telah memperkenalkan bahasa, jawaban saya sekarang akan sangat berbeda, dan lebih sederhana. Dengan Java 8, tidak perlu kelas utilitas statis yang berbeda. Jadi jika Anda ingin menemukan elemen 1 yang cocok dengan predikat Anda.

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

JDK 8 API untuk optionals memiliki kemampuan untuk get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier)dan orElseThrow(exceptionSupplier), serta fungsi lainnya 'monadik' seperti map, flatMapdan filter.

Jika Anda hanya ingin mengumpulkan semua pengguna yang cocok dengan predikat, maka gunakan Collectorsuntuk mengakhiri streaming dalam koleksi yang diinginkan.

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

Lihat di sini untuk contoh lebih lanjut tentang cara kerja stream Java 8.


27
Ya, tapi saya benci untuk menemukan kembali roda, lagi, berulang kali. Saya lebih suka menemukan beberapa perpustakaan utilitas yang melakukannya saat saya mau.
Kevin Wong

2
Ini bukan cara terbaik jika Anda tidak ingin koleksi baru. Gunakan metafora filter iterator, yang dapat dimasukkan ke dalam koleksi baru, atau mungkin hanya itu yang Anda butuhkan.
Josh

@ Nestor: dalam pemahaman Scala, penyaringan akan jauh lebih sederhana:val authorized = for (user <- users if user.isAuthorized) yield user
Alan

Apakah ini mengubah koleksi asli atau membuat yang baru? Saya mencoba menggunakan metode ini dan mencatat koleksi saya (asli dan yang dikembalikan dari metode), semuanya sama. @Alan
Rohan

1
@Rohan, ini tidak dimaksudkan untuk mengubah koleksi asli. Perhatikan bahwa koleksi hasil di atas baru dibangun, dan metode filter menambah koleksi hasil hanya jika predikat berlaku.
Alan

92

Gunakan CollectionUtils.filter (Koleksi, Predikat) , dari Apache Commons.


3
ini tidak apa-apa, tapi tidak generik, dan memodifikasi koleksi di tempatnya (tidak baik)
Kevin Wong

2
Ada metode filter lain di CollectionUtils yang tidak mengubah koleksi asli.
skaffman

42
Secara khusus, metode yang tidak mengubah koleksi di tempat adalah org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)
Eero

5
Dalam Commons Collections v4 ini sekarang menggunakan Generics.
Justin Emery

1
Metode ini harus digunakan dengan hati-hati karena bergantung (setidaknya dalam implementasi commons-collections-3.2.1) pada metode iterator.remove () yang opsional untuk koleksi, jadi alih-alih memfilter, katakanlah, sebuah array, Anda mungkin dapatkan UnsupportedOperationException.
user2417480

67

Cara "Terbaik" adalah permintaan yang terlalu luas. Apakah itu "terpendek"? "Tercepat"? "Dapat dibaca"? Saring di tempat atau ke koleksi lain?

Cara paling sederhana (tetapi tidak paling mudah dibaca) adalah dengan mengulanginya dan menggunakan metode Iterator.remove ():

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

Sekarang, agar lebih mudah dibaca, Anda dapat membungkusnya menjadi metode utilitas. Kemudian ciptakan antarmuka IPredicate, buat implementasi anonim dari antarmuka itu dan lakukan sesuatu seperti:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

di mana filterInPlace () iterate collection dan panggil Predicate.keepIt () untuk mengetahui apakah instance disimpan dalam koleksi.

Saya tidak benar-benar melihat pembenaran untuk membawa perpustakaan pihak ketiga hanya untuk tugas ini.


6
Pilihan saya berlaku untuk yang satu ini: hanya berfungsi, tanpa perpustakaan eksternal. Saya tidak pernah menyadari bahwa instantiating Iterator sebenarnya dapat bermanfaat dibandingkan dengan menggunakan sintaks untuk masing-masing, atau bahwa Anda dapat menghapus item dari daftar tanpa ConcurrentModificationException atau sesuatu seperti itu. :)
ZeroOne

1
Saya pikir ini adalah cara terbaik menggunakan lib Java standar tanpa menyalin. Untuk 1,8 akan ada stream()fitur, tetapi tidak semua orang bisa bermain dengan mainan terbaru: P
Populus

Apakah ini memodifikasi koleksi asli juga? @ZeroOne
Rohan

Ya tentu saja, @Rohan. Cobalah jika Anda tidak percaya. ;)
ZeroOne

Haha saya lakukan! Tetapi saya ingin mempertahankan koleksi asli saya. Bisakah Anda menyarankan cara untuk melakukan ini tanpa menambahkan lib eksternal? @ZeroOne
Rohan

62

Pertimbangkan Google Collections untuk kerangka kerja Koleksi yang diperbarui yang mendukung obat generik.

UPDATE : Pustaka koleksi google sekarang sudah tidak digunakan lagi. Anda harus menggunakan rilis terbaru dari Jambu Biji sebagai gantinya. Itu masih memiliki semua ekstensi yang sama ke kerangka koleksi termasuk mekanisme untuk penyaringan berdasarkan predikat.


ya, saya tahu tentang lib koleksi Google. Versi yang saya gunakan tidak memiliki Collections2 di dalamnya. Saya menambahkan jawaban baru untuk pertanyaan ini yang mencantumkan metode khusus.
Kevin Wong

7
Kevin, Iterables.filter () dan Iterators.filter () telah ada sejak awal, dan biasanya semua yang Anda butuhkan.
Kevin Bourrillion

28

Tunggu Java 8:

List<Person> olderThan30 = 
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());

13
Ugh ... sangat verbose. Mengapa mereka tidak bisa begitu saja: Daftar <Person> result = personList.filter (p -> p.age> 30);
Kevin Wong

8
Untuk menggunakan filter langsung pada Collection, Anda perlu menggunakan removeIf call: download.java.net/jdk8/docs/api/java/util/…
gavenkoa

6
@KevinWong "verbose" menggambarkan hampir seluruh bahasa yang saya pikir. Setidaknya mereka konsisten?
Rogue

5
Mengapa tidak menggunakan Collectors.toList () di bagian terakhir?
Nestor Hernandez Loli

3
Berikut adalah tautan gavenkoa asalkan tidak 404. personList.removeIf(p -> p.age < 30);Kurang bertele-tele. Juga, saya pernah mendengar pembicaraan tentang mulai menerapkan apis yang menerima dan mengembalikan Streams daripada Collections karena Streams sangat berguna dan cepat tetapi pergi ke / dari mereka adalah lambat.
Kapten Man

11

Sejak rilis awal Java 8, Anda dapat mencoba sesuatu seperti:

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

Misalnya, jika Anda memiliki daftar bilangan bulat dan Anda ingin memfilter angka yang> 10 lalu mencetak angka-angka itu ke konsol, Anda bisa melakukan sesuatu seperti:

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);

11

Saya akan melempar RxJava di atas ring, yang juga tersedia di Android . RxJava mungkin tidak selalu menjadi pilihan terbaik, tetapi itu akan memberi Anda lebih banyak fleksibilitas jika Anda ingin menambahkan lebih banyak transformasi pada koleksi Anda atau menangani kesalahan saat memfilter.

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
    .filter(new Func1<Integer, Boolean>() {
        public Boolean call(Integer i) {
            return i % 2 != 0;
        }
    })
    .subscribe(new Action1<Integer>() {
        public void call(Integer i) {
            System.out.println(i);
        }
    });

Keluaran:

1
3
5

Rincian lebih lanjut tentang RxJava filterdapat ditemukan di sini .


7

Pengaturan:

public interface Predicate<T> {
  public boolean filter(T t);
}

void filterCollection(Collection<T> col, Predicate<T> predicate) {
  for (Iterator i = col.iterator(); i.hasNext();) {
    T obj = i.next();
    if (predicate.filter(obj)) {
      i.remove();
    }
  }
}

Penggunaan:

List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});

2
Baik, tapi saya lebih suka implementasi Alan karena Anda mendapatkan salinan koleksi alih-alih mengubahnya. Selain itu, kode Alan adalah utas aman sedangkan kode Anda tidak.
marcospereira

7

Bagaimana dengan Jawa biasa dan lurus?

 List<Customer> list ...;
 List<Customer> newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

Sederhana, mudah dibaca dan mudah (dan berfungsi di Android!) Tetapi jika Anda menggunakan Java 8, Anda dapat melakukannya dalam satu baris yang manis:

List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

Perhatikan bahwa toList () diimpor secara statis



7

Mari kita lihat cara memfilter Daftar JDK bawaan dan MutableList menggunakan Eclipse Collections .

List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);

Jika Anda ingin memfilter angka kurang dari 3, Anda akan mengharapkan output berikut.

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

Inilah cara Anda dapat memfilter menggunakan Java 8 lambda sebagai file Predicate.

Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));

Assert.assertEquals(selected, ecList.select(each -> each < 3));
Assert.assertEquals(rejected, ecList.reject(each -> each < 3));

Inilah cara Anda bisa memfilter menggunakan kelas dalam anonim sebagai Predicate.

Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
    public boolean accept(Integer each)
    {
        return each < 3;
    }
};

Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));
Assert.assertEquals(selected, ecList.select(lessThan3));

Berikut adalah beberapa alternatif untuk memfilter daftar JDK dan Eclipse Collections MutableLists menggunakan pabrik Predicates .

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

Berikut adalah versi yang tidak mengalokasikan objek untuk predikat, dengan menggunakan pabrik Predicates2 sebagai gantinya dengan selectWithmetode yang mengambil a Predicate2.

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

Terkadang Anda ingin memfilter pada kondisi negatif. Ada metode khusus dalam Eclipse Collections untuk itu reject.

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));

Metode partitionakan mengembalikan dua koleksi, berisi elemen yang dipilih oleh dan ditolak oleh Predicate.

PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());

PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());

Catatan: Saya pengendara untuk Eclipse Collections.


1
Bagaimana Anda akan melakukan removeIfpada daftar atau menetapkan untuk primitif?
Vivek Rao

API untuk removeIf ditambahkan ke koleksi primitif di EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Donald Raab

5

Dengan ForEach DSL Anda dapat menulis

import static ch.akuhn.util.query.Query.select;
import static ch.akuhn.util.query.Query.$result;
import ch.akuhn.util.query.Select;

Collection<String> collection = ...

for (Select<String> each : select(collection)) {
    each.yield = each.value.length() > 3;
}

Collection<String> result = $result();

Diberikan koleksi [The, quick, brown, fox, jumps, over, the, lazy, dog] ini menghasilkan [quick, brown, jumps, over, lazy], yaitu semua string lebih panjang dari tiga karakter.

Semua gaya iterasi yang didukung oleh ForEach DSL adalah

  • AllSatisfy
  • AnySatisfy
  • Collect
  • Counnt
  • CutPieces
  • Detect
  • GroupedBy
  • IndexOf
  • InjectInto
  • Reject
  • Select

Untuk detail lebih lanjut, silakan merujuk ke https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach


Itu cukup pintar! Namun, banyak pekerjaan untuk mengimplementasikan sintaksis Ruby-ish yang bagus! Negatifnya adalah filter Anda bukan fungsi kelas satu dan karenanya tidak dapat digunakan kembali. Gulung pada penutupan ...
oxbow_lakes

Poin bagus. Salah satu cara untuk menggunakan kembali tubuh loop adalah dengan refactoring loop ke metode yang mengambil kueri pemilihan sebagai parameter. Namun itu sejauh ini tidak berguna dan kuat seperti penutupan nyata, pasti.
akuhn


5

Karena java 9 Collectors.filtering diaktifkan:

public static <T, A, R>
    Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

Jadi penyaringan harus:

collection.stream().collect(Collectors.filtering(predicate, collector))

Contoh:

List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));

3

Ini, dikombinasikan dengan kurangnya penutupan nyata, adalah keluhan terbesar saya untuk Jawa. Jujur, sebagian besar metode yang disebutkan di atas cukup mudah dibaca dan BENAR-BENAR efisien; namun, setelah menghabiskan waktu dengan .Net, Erlang, dll. Pemahaman daftar yang terintegrasi pada tingkat bahasa membuat semuanya jauh lebih bersih. Tanpa penambahan di tingkat bahasa, Java tidak akan sebersih banyak bahasa lain di area ini.

Jika kinerja adalah masalah besar, koleksi Google adalah cara untuk pergi (atau menulis utilitas predikat sederhana Anda sendiri). Sintaks Lambdaj lebih mudah dibaca oleh sebagian orang, tetapi tidak cukup efisien.

Dan kemudian ada perpustakaan yang saya tulis. Saya akan mengabaikan pertanyaan sehubungan dengan efisiensinya (ya, itu seburuk itu) ...... Ya, saya tahu itu jelas berdasarkan refleksi, dan tidak, saya tidak benar-benar menggunakannya, tetapi ia bekerja:

LinkedList<Person> list = ......
LinkedList<Person> filtered = 
           Query.from(list).where(Condition.ensure("age", Op.GTE, 21));

ATAU

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");

Tautan? Bahkan jika perpustakaan Anda tidak efisien atau tidak dapat digunakan, mungkin menarik untuk melihat apakah sumbernya tersedia.
MatrixFrog

Menjadikan repo menjadi publik ( net-machine.com/indefero/p/jdclib/source/tree/master ). Anda tertarik pada paket ekspresi. Paket tes memiliki tester dengan contoh penggunaan. Saya tidak pernah benar-benar melakukan banyak pekerjaan pada antarmuka kueri string yang dirujuk di atas (tidak merasa seperti menulis parser nyata), jadi antarmuka kueri eksplisit dalam tester adalah cara untuk pergi.
jdc0589

2

JFilter http://code.google.com/p/jfilter/ paling cocok untuk kebutuhan Anda.

JFilter adalah pustaka sumber terbuka yang sederhana dan berkinerja tinggi untuk meminta koleksi kacang Jawa.

Fitur utama

  • Mendukung koleksi (java.util.Collection, java.util.Map dan Array) properti.
  • Dukungan koleksi di dalam koleksi kedalaman apa pun.
  • Mendukung pertanyaan dalam.
  • Dukungan kueri parameterisasi.
  • Dapat memfilter 1 juta catatan dalam beberapa 100 ms.
  • Filter (query) diberikan dalam format json sederhana, itu seperti pertanyaan Mangodb. Berikut ini beberapa contohnya.
  • {"id": {"$ le": "10"}
    • di mana properti id objek kurang dari sama dengan 10.
  • {"id": {"$ in": ["0", "100"]}}
    • di mana properti id objek adalah 0 atau 100.
  • {"lineItems": {"lineAmount": "1"}}
    • di mana properti koleksi lineItems dari tipe parameter memiliki lineAmount sama dengan 1.
  • {"$ and": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • di mana properti id adalah 0 dan properti billingAddress.city adalah DEL.
  • {"lineItems": {"tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}}}
    • di mana properti koleksi lineItems dari tipe parameter yang memiliki properti tipe peta pajak dari tipe parameterized memiliki kode sama dengan nilai GST lebih besar dari 1,01.
  • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
    • Pilih semua produk dengan kode produk 10 atau harga sku 20 dan 40 dan kode sku "RedApple".

1
Anda harus menyangkal bahwa Anda adalah penulis (karena saya pikir itu masalahnya).
assylias

Ya, saya penulis perpustakaan ini.
Kamran Ali Khan

2

Saya menulis kelas Iterable diperpanjang yang mendukung penerapan algoritma fungsional tanpa menyalin konten koleksi.

Pemakaian:

List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }

Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
{
    public Boolean call(Integer n) throws FunctionalException
    {
        return n % 2 == 0;
    }
})

for( int n : filtered )
{
    System.out.println(n);
}

Kode di atas sebenarnya akan dieksekusi

for( int n : myList )
{
    if( n % 2 == 0 ) 
    {
        System.out.println(n);
    }
}


2

Beberapa jawaban bagus di sini. Saya, saya ingin menjaga agar tetap sederhana dan mudah dibaca:

public abstract class AbstractFilter<T> {

    /**
     * Method that returns whether an item is to be included or not.
     * @param item an item from the given collection.
     * @return true if this item is to be included in the collection, false in case it has to be removed.
     */
    protected abstract boolean excludeItem(T item);

    public void filter(Collection<T> collection) {
        if (CollectionUtils.isNotEmpty(collection)) {
            Iterator<T> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (excludeItem(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    }
}

Cukup terapkan excludeItem per filter yang tepat. Anda akhirnya memiliki filter terpisah persis seperti Anda memiliki penyortir di Koleksi ...
Lawrence

1

Solusi pra-Java8 sederhana:

ArrayList<Item> filtered = new ArrayList<Item>(); 
for (Item item : items) if (condition(item)) filtered.add(item);

Sayangnya solusi ini tidak sepenuhnya generik, menghasilkan daftar daripada jenis koleksi yang diberikan. Juga, membawa pustaka atau fungsi penulisan yang membungkus kode ini sepertinya berlebihan bagi saya kecuali kondisinya rumit, tetapi kemudian Anda dapat menulis fungsi untuk kondisinya.


1

https://code.google.com/p/joquery/

Mendukung berbagai kemungkinan,

Koleksi yang diberikan,

Collection<Dto> testList = new ArrayList<>();

dari jenis,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

Saring

Java 7

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property("id").eq().value(1);
Collection<Dto> filtered = query.list();

Java 8

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property(Dto::getId)
    .eq().value(1);
Collection<Dto> filtered = query.list();

Juga,

Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .property(Dto::getId).between().value(1).value(2)
        .and()
        .property(Dto::grtText).in().value(new string[]{"a","b"});

Penyortiran (juga tersedia untuk Java 7)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

Pengelompokan (juga tersedia untuk Java 7)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

Bergabung (juga tersedia untuk Java 7)

Diberikan,

class LeftDto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

class RightDto
{
    private int id;
    private int leftId;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getLeftId()
        {
            return leftId;
        }

    public int getText()
    {
        return text;
    }
}

class JoinedDto
{
    private int leftId;
    private int rightId;
    private String text;

    public JoinedDto(int leftId,int rightId,String text)
    {
        this.leftId = leftId;
        this.rightId = rightId;
        this.text = text;
    }

    public int getLeftId()
    {
        return leftId;
    }

    public int getRightId()
        {
            return rightId;
        }

    public int getText()
    {
        return text;
    }
}

Collection<LeftDto> leftList = new ArrayList<>();

Collection<RightDto> rightList = new ArrayList<>();

Dapat Bergabung seperti,

Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                .on(LeftFyo::getId, RightDto::getLeftId)
                .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                     , selection.getLeft().getId()
                                                     , selection.getRight().getId())
                                 )
                .list();

Ekspresi

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);

1

Jawaban saya didasarkan pada hal itu dari Kevin Wong, di sini sebagai penggunaan satu-baris CollectionUtilsdari musim semi dan ekspresi lambda Java 8 .

CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);

Ini ringkas dan mudah dibaca seperti alternatif yang saya lihat (tanpa menggunakan pustaka berbasis aspek)

Spring CollectionUtils tersedia dari versi semi 4.0.2.RELEASE, dan ingat Anda membutuhkan JDK 1.8 dan level bahasa 8+.


1

Menggunakan java 8, secara khusus lambda expression, Anda dapat melakukannya cukup seperti contoh di bawah ini:

myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())

di mana untuk setiap koleksi productdi dalam myProducts, jika prod.price>10, kemudian tambahkan produk ini ke daftar yang difilter baru.


1

Saya perlu memfilter daftar tergantung pada nilai yang sudah ada dalam daftar. Misalnya, hapus semua nilai berikut yang kurang dari nilai saat ini. {2 5 3 4 7 5} -> {2 5 7}. Atau misalnya untuk menghapus semua duplikat {3 5 4 2 3 5 6} -> {3 5 4 2 6}.

public class Filter {
    public static <T> void List(List<T> list, Chooser<T> chooser) {
        List<Integer> toBeRemoved = new ArrayList<>();
        leftloop:
        for (int right = 1; right < list.size(); ++right) {
            for (int left = 0; left < right; ++left) {
                if (toBeRemoved.contains(left)) {
                    continue;
                }
                Keep keep = chooser.choose(list.get(left), list.get(right));
                switch (keep) {
                    case LEFT:
                        toBeRemoved.add(right);
                        continue leftloop;
                    case RIGHT:
                        toBeRemoved.add(left);
                        break;
                    case NONE:
                        toBeRemoved.add(left);
                        toBeRemoved.add(right);
                        continue leftloop;
                }
            }
        }

        Collections.sort(toBeRemoved, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        for (int i : toBeRemoved) {
            if (i >= 0 && i < list.size()) {
                list.remove(i);
            }
        }
    }

    public static <T> void List(List<T> list, Keeper<T> keeper) {
        Iterator<T> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (!keeper.keep(iterator.next())) {
                iterator.remove();
            }
        }
    }

    public interface Keeper<E> {
        boolean keep(E obj);
    }

    public interface Chooser<E> {
        Keep choose(E left, E right);
    }

    public enum Keep {
        LEFT, RIGHT, BOTH, NONE;
    }
}

Ini akan digunakan seperti ini.

List<String> names = new ArrayList<>();
names.add("Anders");
names.add("Stefan");
names.add("Anders");
Filter.List(names, new Filter.Chooser<String>() {
    @Override
    public Filter.Keep choose(String left, String right) {
        return left.equals(right) ? Filter.Keep.LEFT : Filter.Keep.BOTH;
    }
});

0

Dengan jambu biji:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5

0

Di Java 8, Anda bisa langsung menggunakan metode filter ini dan kemudian melakukannya.

 List<String> lines = Arrays.asList("java", "pramod", "example");

 List<String> result = lines.stream()              
         .filter(line -> !"pramod".equals(line))     
         .collect(Collectors.toList());              

 result.forEach(System.out::println); 
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.