วิธีที่ดีที่สุดในการกรอง Java Collection คืออะไร?


คำตอบ:


699

Java 8 ( 2014 ) แก้ปัญหานี้โดยใช้สตรีมและ lambdas ในโค้ดหนึ่งบรรทัด:

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

นี่คือการกวดวิชา

ใช้Collection#removeIfเพื่อปรับเปลี่ยนคอลเลกชันในสถานที่ (หมายเหตุ: ในกรณีนี้เพรดิเคตจะลบออบเจกต์ที่ตอบสนองเพรดิเคต):

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

lambdajอนุญาตการกรองคอลเลกชันโดยไม่ต้องเขียนลูปหรือคลาสภายใน:

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

คุณลองนึกภาพสิ่งที่อ่านง่ายขึ้นได้ไหม

ข้อจำกัดความรับผิดชอบ:ฉันเป็นผู้สนับสนุนใน lambdaj


34
ดี แต่การนำเข้าคงทำให้งงงวยอะไรเกิดขึ้น สำหรับการอ้างอิงให้เลือก / having / on เป็นการนำเข้าแบบคงที่ใน ch.lambdaj.Lambda ส่วนใหญ่จะเท่ากับ org.hamcrest.Matchers
MikePatel

11
lambdaj เซ็กซี่จริงๆ แต่มัน worths สังเกตว่ามันหมายถึงค่าใช้จ่ายอย่างมีนัยสำคัญ (2.6 เฉลี่ย): code.google.com/p/lambdaj/wiki/PerformanceAnalysis
Doc Davluz

7
เห็นได้ชัดว่าใช้งานไม่ได้กับ Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ
Moritz

7
ชอบตัวอย่างของ LamdaJ นี้ ... คล้ายกับ. NET ในตัวของฟังก์ชัน Lambda และคนที่สามารถดื่มเมื่ออายุ 16 เราควรพิจารณาเพิ่มข้อ จำกัด การแปล : P
MAbraham1

3
removeIf ตัวอย่างเช่นควรจะเป็นpersons.removeIf(p -> p.getAge() <= 16);
เป็นกลุ่ม

223

สมมติว่าคุณใช้Java 1.5และคุณไม่สามารถเพิ่มGoogle Collectionsฉันจะทำสิ่งที่คล้ายกับที่ Google ทำ นี่เป็นความแตกต่างเล็กน้อยในความคิดเห็นของจอน

เพิ่มอินเทอร์เฟซนี้ไปยัง codebase ของคุณก่อน

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

ผู้ใช้งานสามารถตอบเมื่อภาคแสดงบางอย่างเป็นจริงของบางประเภท เช่นถ้าTมีUserและAuthorizedUserPredicate<User>ดำเนินการIPredicate<T>แล้วAuthorizedUserPredicate#applyผลตอบแทนที่ไม่ว่าจะผ่านUserได้รับอนุญาต

จากนั้นในคลาสยูทิลิตี้บางอย่างคุณสามารถพูดได้

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

ดังนั้นสมมติว่าคุณมีการใช้งานด้านบนอาจจะเป็น

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

หากประสิทธิภาพในการตรวจสอบแบบเชิงเส้นมีความกังวลฉันอาจต้องการมีวัตถุโดเมนที่มีการรวบรวมเป้าหมาย วัตถุโดเมนที่มีการรวบรวมเป้าหมายจะมีการกรองตรรกะสำหรับวิธีการที่เริ่มต้นเพิ่มและตั้งค่าการเก็บรวบรวมเป้าหมาย

UPDATE:

ในคลาสยูทิลิตี้ (สมมุติว่าเพรดิเคต) ฉันได้เพิ่มเมธอด select พร้อมตัวเลือกสำหรับค่าเริ่มต้นเมื่อเพรดิเคตไม่ส่งคืนค่าที่คาดไว้และยังเป็นคุณสมบัติคงที่สำหรับ params ที่จะใช้ภายใน IPredicate ใหม่

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

ตัวอย่างต่อไปนี้ค้นหาวัตถุที่หายไประหว่างคอลเลกชัน:

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

ตัวอย่างต่อไปนี้ค้นหาอินสแตนซ์ในคอลเลกชันและส่งกลับองค์ประกอบแรกของคอลเลกชันเป็นค่าเริ่มต้นเมื่อไม่พบอินสแตนซ์:

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

อัปเดต (หลังจากรีลีส Java 8):

เป็นเวลาหลายปีแล้วที่ฉัน (อลัน) โพสต์คำตอบนี้เป็นครั้งแรกและฉันก็ยังไม่เชื่อว่าฉันกำลังรวบรวมคะแนน SO สำหรับคำตอบนี้ ไม่ว่าอย่างไรก็ตาม ณ ขณะนี้ที่ Java 8 ได้แนะนำการปิดภาษาคำตอบของฉันตอนนี้จะแตกต่างกันมากและง่ายขึ้น ด้วย Java 8 ไม่จำเป็นต้องมีคลาสยูทิลิตี้สแตติกที่แตกต่างกัน ดังนั้นหากคุณต้องการค้นหาองค์ประกอบที่ 1 ที่ตรงกับภาคแสดงของคุณ

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 สำหรับ optionals มีความสามารถในการget(), isPresent(), orElse(defaultUser), orElseGet(userSupplier)และorElseThrow(exceptionSupplier)เช่นเดียวกับคนอื่น ๆ 'เอก' ฟังก์ชั่นเช่นmap, และflatMapfilter

หากคุณต้องการรวบรวมผู้ใช้ทั้งหมดที่ตรงกับเพรดิเคตนั้นให้ใช้Collectorsเพื่อยุติการสตรีมในการรวบรวมที่ต้องการ

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

ดูที่นี่สำหรับตัวอย่างเพิ่มเติมเกี่ยวกับการทำงานของสตรีม Java 8


27
ใช่ แต่ฉันเกลียดที่จะคิดค้นใหม่ล้อซ้ำแล้วซ้ำอีก ฉันควรหาห้องสมุดสาธารณูปโภคที่ต้องการ
เควินหว่อง

2
นี่ไม่ใช่วิธีที่ดีที่สุดในกรณีที่คุณไม่ต้องการคอลเล็กชันใหม่ ใช้คำอุปมาตัวทำซ้ำตัวกรองซึ่งอาจป้อนลงในคอลเลกชันใหม่หรืออาจเป็นสิ่งที่คุณต้องการ
Josh

@Nestor: ใน Scala comprehension การกรองจะง่ายกว่ามาก:val authorized = for (user <- users if user.isAuthorized) yield user
Alan

สิ่งนี้จะแก้ไขคอลเล็กชันดั้งเดิมหรือสร้างใหม่หรือไม่ ฉันลองใช้วิธีนี้และบันทึกทั้งคอลเลกชันของฉัน (ต้นฉบับและที่ส่งคืนจากวิธีการ) พวกเขาเหมือนกัน @Alan
Rohan

1
@Rohan นี่ไม่ได้หมายถึงการกลายพันธุ์คอลเลกชันดั้งเดิม โปรดทราบว่าการรวบรวมผลลัพธ์ด้านบนสร้างขึ้นใหม่และวิธีการกรองจะเพิ่มในการรวบรวมผลลัพธ์เฉพาะในกรณีที่คำกริยานั้นใช้
Alan

92

ใช้CollectionUtils.filter (Collection, Predicate)จาก Apache Commons


3
นี้จะถูก แต่ก็ไม่ทั่วไปและปรับเปลี่ยนคอลเลกชันในสถานที่ (ไม่ดี)
เควินวงศ์

2
มีวิธีการกรองอื่น ๆ ใน CollectionUtils ที่ไม่ได้แก้ไขการรวบรวมเดิม
skaffman

42
โดยเฉพาะอย่างยิ่งวิธีการที่ไม่ได้แก้ไขการเก็บในสถานที่คือ org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)
Eero

5
ใน Commons Collections v4 ตอนนี้ใช้ Generics
Justin Emery

1
ควรใช้วิธีนี้ด้วยความระมัดระวัง (อย่างน้อยในการใช้งานคอมมอนส์ - คอลเลกชัน - 3.2.1) ใน iterator.remove () วิธีการซึ่งเป็นตัวเลือกสำหรับคอลเลกชันดังนั้นแทนที่จะกรองพูดอาร์เรย์คุณอาจ รับ UnsupportedOperationException
user2417480

67

วิธีที่ "ดีที่สุด" เป็นการร้องขอที่กว้างเกินไป มันเป็น "สั้น" หรือไม่? "เร็วที่สุด"? "อ่าน"? กรองในสถานที่หรือในคอลเลกชันอื่นหรือไม่

วิธีที่ง่ายที่สุด (แต่ไม่สามารถอ่านได้มากที่สุด) คือทำซ้ำและใช้เมธอด Iterator.remove ():

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

ตอนนี้เพื่อให้สามารถอ่านได้มากขึ้นคุณสามารถห่อมันเป็นวิธีการยูทิลิตี้ จากนั้นคิดค้นอินเทอร์เฟซ IPredicate สร้างการใช้งานที่ไม่ระบุชื่อของอินเทอร์เฟซนั้นและทำสิ่งที่ชอบ:

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

โดยที่ filterInPlace () วนซ้ำคอลเล็กชันและเรียกใช้ Predicate.keepIt () เพื่อเรียนรู้ว่าอินสแตนซ์ที่จะเก็บไว้ในคอลเล็กชัน

ฉันไม่เห็นเหตุผลในการนำห้องสมุดบุคคลที่สามมาใช้สำหรับงานนี้


6
คะแนนของฉันไปสำหรับสิ่งนี้: มันใช้ได้ แต่ไม่มีห้องสมุดภายนอก ฉันไม่เคยรู้เลยว่าการใช้ Iterator นั้นมีประโยชน์จริง ๆ เมื่อเทียบกับการใช้ไวยากรณ์สำหรับแต่ละครั้งหรือคุณสามารถลบรายการออกจากรายการโดยไม่ต้องมี ConcurrentModificationException หรืออะไรทำนองนั้น :)
ZeroOne

1
ฉันคิดว่านี่เป็นวิธีที่ดีที่สุดในการใช้ lib Java มาตรฐานโดยไม่ต้องคัดลอก สำหรับ 1.8 จะstream()มีฟีเจอร์ แต่ทุกคนจะไม่เล่นกับของเล่นใหม่ล่าสุด: P
Populus

สิ่งนี้แก้ไขคอลเล็กชันดั้งเดิมด้วยหรือไม่ @ZeroOne
Rohan

ใช่แน่นอนมันทำ @Rohan ลองทำถ้าคุณไม่เชื่อ ;)
ZeroOne

ฮ่าฮ่าฉันทำ! แต่ฉันต้องการเก็บต้นฉบับของฉันไว้ คุณสามารถแนะนำวิธีการนี้ได้โดยไม่ต้องเพิ่ม lib ภายนอกหรือไม่? @ZeroOne
Rohan

62

พิจารณาGoogle Collectionsสำหรับเฟรมเวิร์กคอลเล็กชันที่อัปเดตซึ่งรองรับ generics

อัปเดต : ห้องสมุด google collection เลิกใช้แล้ว คุณควรใช้Guavaรุ่นล่าสุดแทน มันยังคงมีนามสกุลเดียวกันทั้งหมดในเฟรมเวิร์กคอลเล็กชันรวมถึงกลไกการกรองตามเพรดิเคต


ใช่ฉันรู้เกี่ยวกับ lib คอลเล็กชัน Google รุ่นที่ฉันใช้ไม่ได้มี Collections2 อยู่ในนั้น ฉันได้เพิ่มคำตอบใหม่สำหรับคำถามนี้ซึ่งแสดงวิธีการเฉพาะ
เควินหว่อง

7
Kevin, Iterables.filter () และ Iterators.filter () มาจากจุดเริ่มต้นและมักจะเป็นสิ่งที่คุณต้องการ
Kevin Bourrillion

28

รอ 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
อืม ... มันละเอียดมาก ทำไมพวกเขาทำไม่ได้: รายการ <Person> result = personList.filter (p -> p.age> 30);
เควินหว่อง

8
ในการใช้ตัวกรองโดยตรงบนคอลเลกชันคุณต้องใช้remove ถ้าโทร: download.java.net/jdk8/docs/api/java/util/ …
gavenkoa

6
@KevinWong "verbose" ค่อนข้างจะอธิบายถึงภาษาทั้งหมดที่ฉันคิด อย่างน้อยพวกเขาก็สอดคล้องกัน?
Rogue

5
ทำไมไม่ใช้ Collector.toList () ในส่วนสุดท้าย?
Nestor Hernandez Loli

3
นี่คือลิงค์ gavenkoa ที่จัดไว้ให้ซึ่งไม่ได้ 404 personList.removeIf(p -> p.age < 30);verbose น้อยลง นอกจากนี้ฉันเคยได้ยินพูดคุยเกี่ยวกับการเริ่มใช้ apis ที่ยอมรับและส่งคืนStreams แทนCollections เพราะStreamมีประโยชน์มากและรวดเร็ว แต่การไป / จากพวกเขาช้า
Captain Man

11

ตั้งแต่การเปิดตัว Java 8 รุ่นแรกคุณสามารถลองสิ่งต่อไปนี้:

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

ตัวอย่างเช่นหากคุณมีรายการจำนวนเต็มและคุณต้องการกรองตัวเลขที่> 10 แล้วพิมพ์ตัวเลขเหล่านั้นไปที่คอนโซลคุณสามารถทำสิ่งต่อไปนี้:

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

11

ฉันจะโยนRxJavaในวงแหวนซึ่งมีให้ในAndroidด้วย RxJava อาจไม่ใช่ตัวเลือกที่ดีที่สุดเสมอไป แต่มันจะช่วยให้คุณมีความยืดหยุ่นมากขึ้นถ้าคุณต้องการเพิ่มการแปลงเพิ่มเติมในคอลเลกชันของคุณหรือจัดการข้อผิดพลาดขณะกรอง

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

เอาท์พุท:

1
3
5

รายละเอียดเพิ่มเติมเกี่ยว RxJava ของfilterสามารถพบได้ที่นี่


7

การตั้งค่า:

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

การใช้งาน:

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

2
ไม่เป็นไร แต่ฉันชอบการนำ Alan ไปใช้เพราะคุณได้รับสำเนาของคอลเลกชันแทนที่จะทำการเปลี่ยนแปลง นอกจากนี้โค้ดของอลันนั้นปลอดภัยในขณะที่คุณไม่อยู่
marcospereira

7

เกี่ยวกับ Java ธรรมดาและ straighforward บางตัว

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

ง่ายอ่านง่ายและใช้งานได้บน Android! แต่ถ้าคุณใช้ Java 8 คุณสามารถทำได้ด้วยบรรทัดเดียว:

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

โปรดทราบว่า toList () จะถูกนำเข้าแบบคงที่



7

ดู Let 's ที่วิธีการกรองในตัว JDK รายการและMutableListใช้Eclipse คอลเลกชัน

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

หากคุณต้องการกรองตัวเลขน้อยกว่า 3 คุณคาดหวังผลลัพธ์ต่อไปนี้

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

นี่คือวิธีที่คุณสามารถกรองโดยใช้ Java 8 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));

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

นี่คือทางเลือกอื่นในการกรองรายการ JDK และEclipse Collections MutableLists โดยใช้โรงงานPredicates

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

นี่เป็นรุ่นที่ไม่ได้จัดสรรวัตถุสำหรับการวินิจฉัยโดยใช้Predicates2โรงงานแทนกับวิธีการที่ใช้selectWithPredicate2

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

บางครั้งคุณต้องการกรองตามเงื่อนไขเชิงลบ rejectมีวิธีการพิเศษในการคราสคอลเลกชันที่เรียกว่าเป็น

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

วิธีการที่จะกลับมาสองคอลเลกชันที่มีองค์ประกอบที่เลือกโดยและปฏิเสธโดยpartitionPredicate

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

หมายเหตุ: ฉันเป็นคอมมิชชันสำหรับ Eclipse Collections


1
คุณจะทำอย่างไรremoveIfในรายการหรือตั้งค่าสำหรับดั้งเดิม?
Vivek Rao

API สำหรับ remove if ถูกเพิ่มเข้ากับการรวบรวมดั้งเดิมใน EC 9.1 eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…
Donald Raab

5

ด้วย ForEach DSL คุณสามารถเขียนได้

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

ได้รับชุดของ [The, quick, brown, fox, jumps, over, the, lazy, dog] ผลลัพธ์เป็น [quick, brown, jumps, over, lazy] นั่นคือสตริงทั้งหมดยาวกว่าสามอักขระ

รูปแบบการวนซ้ำทั้งหมดที่รองรับโดย ForEach DSL คือ

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

สำหรับรายละเอียดเพิ่มเติมโปรดดูที่https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach


มันค่อนข้างฉลาด! งานจำนวนมากที่ใช้ไวยากรณ์ Ruby-ish ที่ดี! ข้อเสียคือตัวกรองของคุณไม่ใช่ฟังก์ชันชั้นหนึ่งจึงไม่สามารถใช้ซ้ำได้ กลิ้งตัวปิด ...
oxbow_lakes

จุดดี. วิธีหนึ่งในการนำลูป body มาใช้ซ้ำคือการ refactoring ลูปเป็นวิธีที่ใช้คิวรีการเลือกเป็นพารามิเตอร์ อย่างไรก็ตามนั่นไม่ใช่สิ่งที่มีประโยชน์และทรงพลังเท่ากับการปิดตัวจริงอย่างแน่นอน
akuhn


5

ตั้งแต่เปิดใช้งานจาวา 9 Collectors.filtering :

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

ดังนั้นการกรองควรจะ:

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

ตัวอย่าง:

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

3

เมื่อรวมกับการขาดการปิดจริง ๆ แล้วสิ่งนี้เป็นสิ่งที่จับต้องได้มากที่สุดสำหรับ Java จริงๆแล้ววิธีการต่าง ๆ ที่กล่าวมาข้างต้นนั้นค่อนข้างง่ายต่อการอ่านและมีประสิทธิภาพจริงๆ อย่างไรก็ตามหลังจากใช้เวลากับ. Net, Erlang และอื่น ๆ ... ความเข้าใจในรายการรวมเข้ากับระดับภาษาทำให้ทุกอย่างสะอาดขึ้น หากไม่มีการเพิ่มเติมในระดับภาษาจาวาก็คงไม่สะอาดเท่ากับภาษาอื่น ๆ ในพื้นที่นี้

หากประสิทธิภาพเป็นสิ่งที่น่ากังวลอย่างมากคอลเล็กชันของ Google คือหนทางที่จะไป ไวยากรณ์ของแลมบ์ดาสามารถอ่านได้มากขึ้นสำหรับบางคน แต่ก็ไม่ค่อยมีประสิทธิภาพเท่าไหร่

แล้วมีห้องสมุดที่ฉันเขียน ฉันจะเพิกเฉยต่อคำถามใด ๆ เกี่ยวกับประสิทธิภาพของมัน (ใช่แล้วมันแย่มาก) ...... ใช่ฉันรู้ว่ามันมีการสะท้อนที่ชัดเจนและไม่มีฉันไม่ได้ใช้จริง ๆ แต่มันใช้งานได้:

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

หรือ

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

Link? แม้ว่าไลบรารี่ของคุณจะไม่มีประสิทธิภาพหรือไม่สามารถใช้งานได้มันอาจจะน่าสนใจที่จะดูว่ามีแหล่งที่มาหรือไม่
MatrixFrog

ทำให้ repo เป็นสาธารณะ ( net-machine.com/indefero/p/jdclib/source/tree/master ) คุณมีความสนใจในแพคเกจการแสดงออก แพคเกจการทดสอบมีผู้ทดสอบพร้อมตัวอย่างการใช้งาน ฉันไม่เคยทำงานจริง ๆ บนอินเตอร์เฟสการค้นหาสตริงที่อ้างถึงข้างต้น (ไม่รู้สึกเหมือนเขียนตัวแยกวิเคราะห์จริง) ดังนั้นอินเทอร์เฟซการสืบค้นที่ชัดเจนในตัวทดสอบเป็นวิธีที่จะไป
jdc0589

2

JFilter http://code.google.com/p/jfilter/เหมาะสมที่สุดสำหรับความต้องการของคุณ

JFilter เป็นไลบรารี่โอเพนซอร์สที่ง่ายและมีประสิทธิภาพสูงในการสืบค้นคอลเลคชั่นของ Java beans

ฟีเจอร์หลัก

  • สนับสนุนคุณสมบัติ (java.util.Collection, java.util.Map และ Array)
  • รองรับการสะสมในคอลเล็กชั่นทุกระดับความลึก
  • สนับสนุนการสืบค้นภายใน
  • การสนับสนุนการค้นหาพารามิเตอร์
  • สามารถกรอง 1 ล้านบันทึกในไม่กี่ 100 มิลลิวินาที
  • ตัวกรอง (แบบสอบถาม) ได้รับในรูปแบบ json แบบง่ายมันก็เหมือนกับการสืบค้น Mangodb ต่อไปนี้เป็นตัวอย่างบางส่วน
  • {"id": {"$ le": "10"}
    • โดยที่ object id คุณสมบัติน้อยกว่าเท่ากับ 10
  • {"id": {"$ in": ["0", "100"]}}
    • โดยที่ object id คุณสมบัติเป็น 0 หรือ 100
  • { "รายการโฆษณา": { "lineAmount": "1"}}
    • โดยที่คุณสมบัติการรวบรวม lineItems ของประเภทพารามิเตอร์มี lineAmount เท่ากับ 1
  • {"$ และ": [{"id": "0"}, {"billingAddress": {"เมือง": "DEL"}}]}}
    • โดยที่ id คุณสมบัติเป็น 0 และคุณสมบัติ billingAddress.city คือ DEL
  • {"lineItems": {"tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}}}
    • โดยที่คุณสมบัติการรวบรวม lineItems ของประเภทพารามิเตอร์ซึ่งมีคุณสมบัติประเภทแผนที่ภาษีประเภท parameteriszed มีรหัสเท่ากับค่า GST มากกว่า 1.01
  • {'$ หรือ': [{'code': '10'}, {'skus': {'$ และ': [{'price': {'$ in': ['20', '40']} }, {'code': 'RedApple'}]}}]}
    • เลือกผลิตภัณฑ์ทั้งหมดที่มีรหัสผลิตภัณฑ์คือ 10 หรือราคา sku ใน 20 และ 40 และรหัส sku คือ "RedApple"

1
คุณควรปฏิเสธว่าคุณเป็นผู้เขียน (ตามที่ฉันคิดว่ามันเป็นกรณี)
assylias

ใช่ฉันเป็นผู้แต่งห้องสมุดนี้
Kamran Ali Khan

2

ฉันเขียนคลาส Iterable เพิ่มเติมที่สนับสนุนการใช้อัลกอริทึมการทำงานโดยไม่คัดลอกเนื้อหาคอลเลกชัน

การใช้งาน:

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

รหัสข้างต้นจะทำงานจริง

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


2

บางคำตอบที่ยอดเยี่ยมจริงๆที่นี่ ฉันฉันต้องการที่จะรักษาความเรียบง่ายและอ่านง่ายที่สุดเท่าที่จะเป็นไปได้:

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

เพียงใช้งาน excluseItem ที่เหมาะสมต่อตัวกรอง คุณจะได้ตัวกรองแยกต่างหากเหมือนกับที่คุณมี sorter ในคอลเล็กชัน ...
ลอเรนซ์

1

โซลูชัน pre-Java8 ง่าย ๆ :

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

น่าเสียดายที่โซลูชันนี้ไม่ได้เป็นแบบทั่วไปอย่างสมบูรณ์ แต่ให้แสดงรายการมากกว่าประเภทของคอลเลกชันที่กำหนด นอกจากนี้การนำห้องสมุดหรือการเขียนฟังก์ชั่นที่ห่อรหัสนี้ดูเหมือนจะเกินความจริงของฉันเว้นแต่ว่าเงื่อนไขนั้นซับซ้อน แต่คุณสามารถเขียนฟังก์ชันสำหรับเงื่อนไขได้


1

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

รองรับความเป็นไปได้ที่แตกต่างกัน

ให้สะสม

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

ประเภท

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

กรอง

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

นอกจากนี้

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

การเรียงลำดับ (มีให้สำหรับ Java 7)

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

การจัดกลุ่ม (พร้อมใช้งานสำหรับ Java 7)

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

ตัวเชื่อม (มีให้สำหรับ Java 7)

ป.ร. ให้ไว้

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

สามารถเข้าร่วมได้เช่น

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

การแสดงออก

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

1

คำตอบของฉันสร้างจากสิ่งนั้นจากเควินหว่องนี่เป็นหนึ่งซับในที่ใช้CollectionUtilsจากสปริงและนิพจน์แลมบ์ดาของ Java 8

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

นี่คือรัดกุมและสามารถอ่านได้เป็นทางเลือกอื่น ๆ ที่ฉันได้เห็น (โดยไม่ต้องใช้ห้องสมุดตามลักษณะ)

Spring CollectionUtilsมีให้ในรุ่นฤดูใบไม้ผลิ 4.0.2 ปล่อยและจำไว้ว่าคุณต้องมี JDK 1.8 และระดับภาษา 8+


1

การใช้โดยjava 8เฉพาะlambda expressionคุณสามารถทำได้เช่นเดียวกับตัวอย่างด้านล่าง:

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

ที่สำหรับแต่ละคอลเลกชันproductภายในmyProductsถ้าprod.price>10จากนั้นเพิ่มผลิตภัณฑ์นี้ไปยังรายการกรองใหม่


1

ฉันต้องการกรองรายการตามค่าที่มีอยู่แล้วในรายการ ตัวอย่างเช่นลบค่าทั้งหมดตามที่น้อยกว่าค่าปัจจุบัน {2 5 3 4 7 5} -> {2 5 7} หรือตัวอย่างเช่นการลบรายการซ้ำทั้งหมด {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;
    }
}

มันจะใช้ผึ้งแบบนี้

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

ด้วยฝรั่ง:

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

ใน Java 8 คุณสามารถใช้วิธีการตัวกรองนี้โดยตรงแล้วทำเช่นนั้น

 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); 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.