2011-12-26 39 views
22

Trong một chương trình Java, tôi có danh sách các hạt mà tôi muốn lọc dựa trên một thuộc tính cụ thể.Lọc danh sách JavaBeans bằng Google Guava

Ví dụ: giả sử tôi có danh sách Người, JavaBean, trong đó Người có nhiều thuộc tính, trong số đó là 'tên'.

Tôi cũng có danh sách tên.

Bây giờ tôi muốn tìm tất cả những người có tên trong danh sách tên.

Cách tốt nhất để thực thi bộ lọc này bằng Google ổi là gì?

Cho đến nay, tôi đã nghĩ đến việc kết hợp Guava với đậu tương Apache, nhưng điều đó không có vẻ thanh lịch.

Tôi cũng tìm thấy thư viện tiện ích phản chiếu tại đây: http://code.google.com/p/guava-reflection/, nhưng tôi không chắc chắn cách sử dụng thư viện (có ít tài liệu).

Mọi suy nghĩ?

p.s. Bạn có thể nói tôi thực sự bỏ qua việc hiểu danh sách Python không?

Trả lời

42

Làm theo cách cũ, không có ổi. (Nói như một nhà phát triển ổi.)

List<Person> filtered = Lists.newArrayList(); 
for(Person p : allPersons) { 
    if(acceptedNames.contains(p.getName())) { 
     filtered.add(p); 
    } 
} 

Bạn có thể làm điều này với ổi, nhưng Java không phải là Python, và cố gắng để làm cho nó vào Python chỉ sẽ kéo dài đang lúng túng và không thể đọc được. Tiện ích chức năng của ổi nên được sử dụng một cách tiết kiệm, và chỉ khi chúng cung cấp một lợi ích cụ thể và có thể đo lường được cho cả hai dòng mã hoặc hiệu suất.

+0

Sự khác biệt chính giữa giải pháp này và của tôi là giải pháp này tạo ra một danh sách mới chứa những người được lọc, trong khi tôi tạo ra một cái nhìn trên danh sách ban đầu. Cho dù một hay khác là thích hợp phụ thuộc vào trường hợp sử dụng. –

+0

Có, nó phụ thuộc vào trường hợp sử dụng - nhưng tôi muốn nói rằng trong 90% các trường hợp sử dụng, các cải thiện khả năng đọc lớn hơn các lợi ích hiệu suất nhỏ. –

+3

Bạn dường như nghĩ rằng hai lĩnh vực thỏa hiệp duy nhất là khả năng đọc và hiệu suất. Sự đúng đắn quan trọng hơn cả khả năng đọc và hiệu suất (nghĩa là). Nếu bạn làm theo cách này, bạn không chỉ phải kiểm tra logic vị ngữ của mình, mà còn là logic lọc của bạn. Bạn đã tăng gấp đôi không cần thiết (ít nhất là do trạng thái có thể thay đổi được giới thiệu) số lượng những thứ có thể sai. Nhân số này với số lần bạn lọc qua một codebase ... tại sao bạn lại muốn làm việc theo cách này? –

22
Iterable<Person> filtered = Iterables.filter(allPersons, new Predicate<Person>() { 
    @Override 
    public boolean apply(Person p) { 
     return acceptedNames.contains(p.getName()); 
    } 
}); 

Nếu danh sách tên là lớn, bạn nên biến nó thành một Set (HashSet, tốt hơn) và cuộc gọi chứa trên bộ này, chứ không phải là danh sách, bởi vì chứa là O (1) cho một HashSet và O (n) cho một Danh sách.

+1

gì nên được chuyển đổi thành một bộ là danh sách các tên được chấp nhận, không phải là danh sách những người. Đó là những gì chứa được gọi là trên. Việc chuyển đổi danh sách những người thành một tập hợp không có giá trị gia tăng. –

+0

@ Nhận xét của Daniel về câu trả lời khác cung cấp một số ngữ cảnh về lý do tại sao điều này sẽ thích hợp hơn trong một số trường hợp. – studgeek

3

Tôi không thể đồng ý đủ với câu trả lời của Louis và JB. Tôi không biết ổi phản chiếu, có lẽ LambdaJ thể là những gì bạn đang tìm kiếm:

// set up 
Person me = new Person("Favio"); 
Person luca = new Person("Luca"); 
Person biagio = new Person("Biagio"); 
Person celestino = new Person("Celestino"); 
Collection<Person> meAndMyFriends = asList(me, luca, biagio, celestino); 

// magic 
Collection<Person> filtered = filter(having(on(Person.class).getName(), 
              isOneOf("Favio", "Luca")), 
            meAndMyFriends); 

// test 
assertThat(filtered, hasItems(me, luca)); 
assertEquals(2, filtered.size()); 

Hoặc có thể Scala, Clojure hoặc Groovy được những gì bạn đang tìm kiếm ...

5

Giải thích những nghi ngờ của bạn từ câu:

Cho đến nay, tôi đã nghĩ đến việc kết hợp Guava với hạt đậu Apache, nhưng không có vẻ thanh lịch.

Java, mặc dù là rất phổ biến, thiếu first-class function hỗ trợ *, là những gì subject to change in Java 8, nơi bạn sẽ có thể làm:

Iterable <Person> filtered = filter(allPersons, (Person p) -> acceptedNames.contains(p.getName())); 

Với lambdas và nó sẽ được thanh lịch.

Cho đến lúc đó bạn đã lựa chọn giữa:

  • cách cũ-trường (như @Louis viết)
  • lọc verbose Ổi (@ câu trả lời của JB)
  • hoặc các thư viện Java chức năng khác (@ superfav của câu trả lời).

Tôi cũng muốn để thêm vào @ câu trả lời Lois rằng Guava-way would be to create immutable collection, vì they are better than unmodifiable, mà còn được mô tả mục 15, Minimize mutability trong Effective Java bởi Joshua Bloch **:

ImmutableList.Builder<Person> builder = ImmutableList.builder(); 
for (final Person p : allPersons) { 
    if (acceptedNames.contains(p.getName())) { 
     builder.add(p); 
    } 
} 
ImmutableList<Person> filtered = builder.build(); 

(Chi tiết triển khai của nó là ImmutableList.Builder tạo tạm thời ArrayList dưới mui xe).

*: nó làm tôi bực mình nhiều, tôi đến từ Python, JavaScript và Perl thế giới, where functions are treated better

**: Ổi và Bloch là chặt chẽ cùng bằng nhiều cách;)

2

Phát biểu như là nhà phát triển của ổi phản ánh, tôi xin lỗi vì tôi đã từ bỏ dự án này ở giai đoạn đầu như vậy (tôi có một công việc ban ngày và một người vợ & trẻ em :-)). Tầm nhìn của tôi là một cái gì đó như:

Iterable<Object> thingsWithNames = 
    Iterables.filter(someData, 
        // this is a Predicate, obviously 
        BeanProperties.hasBeanProperty("name", String.class)); 

Mã hiện tại khoảng 60% ở đó, vì vậy nếu bạn quan tâm, hãy liên hệ với tôi và có lẽ chúng ta có thể kết thúc với nhau.

0

Nếu bạn sử dụng một LinkedList (hoặc bất kỳ bộ sưu tập khác mà loại bỏ oprations không phải là rất mất thời gian) trong ứng dụng đơn luồng là giải pháp hiệu quả nhất là:

final Iterator<User> userIterator = users.iterator(); 
while (userIterator.hasNext()) { 
    if (/* your condition for exclusion */) { 
     userIterator.remove(); 
    } 
} 
+0

Ouch! điều này sẽ không hoạt động như bạn sẽ chạy trong truy cập đồng thời vào Danh sách –

+0

Cảm ơn bạn! Tôi đã sửa câu trả lời của mình. –

0

Với Java8 phong cách bạn có thể sử dụng dòng + bộ lọc để đạt được mục tiêu của bạn.

persons.stream() 
      .filter(p -> names.contains(p.getName())) 
      .collect(Collectors.toList()); 
0

Với Java8 bạn có thể sử dụng Collection.removeIf()

List<Person> theList = ...; 
theList.removeIf(
    (Person p)->"paul".equals(p.getName()) 
); 

này tất nhiên sẽ sửa đổi danh sách hiện hành.

0

Dưới đây là một ví dụ của việc sử dụng Generics sử dụng ổi, beanutils để lọc danh sách bất kỳ sử dụng yêu cầu trận đấu

/** 
* Filter List 
* 
* @param inputList 
* @param requestMatch 
* @param invokeMethod 
* @return 
*/ 
public static <T> Iterable<T> predicateFilterList(List<T> inputList, final String requestMatch, 
     final String invokeMethod) { 
    Predicate<T> filtered = new Predicate<T>() { 
     @Override 
     public boolean apply(T input) { 
      boolean ok = false; 
      try { 
       ok = BeanUtils.getProperty(input, invokeMethod).equalsIgnoreCase(requestMatch); 
      } 
      catch (Exception e) { 
       e.printStackTrace(); 
      } 
      return ok; 
     } 
    }; 
    return Iterables.filter(inputList, filtered); 
} 
Các vấn đề liên quan