Ổi: Tại sao không có hàm Lists.filter ()?


86

Có lý do gì không

Lists.transform()

nhưng không

Lists.filter()

?

Làm cách nào để lọc danh sách một cách chính xác? tôi có thể dùng

new ArrayList(Collection2.filter())

tất nhiên, nhưng theo cách này không đảm bảo rằng đơn hàng của tôi vẫn như cũ, nếu tôi hiểu đúng.


8
FYI, List.newArrayList (Iterables.filter (...)) thường nhanh hơn ArrayList mới (Collection2.filter (...)). Phương thức khởi tạo ArrayList gọi size () trên tập hợp đã lọc và việc tính toán kích thước yêu cầu bộ lọc được áp dụng cho mọi phần tử của danh sách ban đầu.
Jared Levy

4
@JaredLevy Có thể thay vì List.newArrayList(Iterables.filter(...)), nó nên nói Lists.newArrayList(Iterables.filter(...)) .
Abdull

Câu trả lời:


57

Nó không được triển khai vì nó sẽ làm lộ ra một số lượng lớn các phương thức chậm nguy hiểm, chẳng hạn như #get (chỉ mục) trên chế độ xem Danh sách được trả về (mời lỗi hiệu suất). Và ListIterator cũng sẽ là một khó khăn để triển khai (mặc dù tôi đã gửi một bản vá cách đây nhiều năm để giải quyết vấn đề đó).

Vì các phương thức được lập chỉ mục không thể hiệu quả trong chế độ xem Danh sách đã lọc, nên tốt hơn là chỉ sử dụng một Lặp có thể được lọc, không có chúng.


7
Bạn đang giả định rằng một chế độ xem danh sách sẽ được trả về. Tuy nhiên, #filter có thể được triển khai dưới dạng trả về một danh sách cụ thể hóa mới, đây thực sự là những gì tôi mong đợi từ một phương pháp lọc cho danh sách trái ngược với phương pháp trên Iterable.
Felix Leipold

@FelixLeipold Tuy nhiên, điều này sẽ làm bùn nước. Như vậy, filternhất quán có nghĩa là một lượt xem (cùng với hành vi ngụ ý) cho dù đó là Iterables.filter, Sets.filterv.v. Vì Iterables.filterdễ dàng kết hợp với copyOfbất kỳ ImmutableCollection, tôi thấy đây là một sự đánh đổi thiết kế tốt (so với việc đưa ra các phương pháp và tên bổ sung, giống như filteredCopyhoặc không , cho sự kết hợp của các tiện ích đơn giản).
Luke Usherwood

37

Bạn có thể sử dụng Iterables.filter, điều này chắc chắn sẽ duy trì đặt hàng.

Lưu ý rằng bằng cách tạo một danh sách mới, bạn sẽ sao chép các phần tử (tất nhiên chỉ là các tham chiếu) - vì vậy nó sẽ không phải là một chế độ xem trực tiếp vào danh sách ban đầu. Tạo một chế độ xem sẽ khá khó khăn - hãy xem xét tình huống này:

Predicate<StringBuilder> predicate = 
    /* predicate returning whether the builder is empty */
List<StringBuilder> builders = Lists.newArrayList();
List<StringBuilder> view = Lists.filter(builders, predicate);

for (int i = 0; i < 10000; i++) {
    builders.add(new StringBuilder());
}
builders.get(8000).append("bar");

StringBuilder firstNonEmpty = view.get(0);

Điều đó sẽ phải lặp lại toàn bộ danh sách ban đầu, áp dụng bộ lọc cho mọi thứ. Tôi cho rằng nó có thể yêu cầu đối sánh vị từ không thay đổi trong suốt thời gian tồn tại của chế độ xem, nhưng điều đó sẽ không hoàn toàn hài lòng.

(Đây chỉ là phỏng đoán, phiền bạn. Có thể một trong những người bảo trì Ổi sẽ đưa ra lý do thực sự :)


1
Collections2.filter.iteratorchỉ gọi Iterables.filter, vì vậy kết quả là như nhau.
skaffman

@skaffman: Trong trường hợp đó, tôi sẽ sử dụng Iterables.filterphiên bản chỉ cho rõ ràng.
Jon Skeet

3
... trừ khi bạn cần một view.size()nơi nào đó sau này trong mã :)
Xaerxess

28

Tất nhiên là tôi có thể sử dụng new List(Collection2.filter()), nhưng cách này không đảm bảo rằng đơn hàng của tôi vẫn như cũ.

Điều này không đúng. Collections2.filter()là một chức năng được đánh giá một cách lười biếng - nó không thực sự lọc bộ sưu tập của bạn cho đến khi bạn bắt đầu truy cập vào phiên bản đã lọc. Ví dụ: nếu bạn lặp lại phiên bản đã lọc, thì các phần tử được lọc sẽ bật ra khỏi trình lặp theo thứ tự như bộ sưu tập ban đầu của bạn (hiển nhiên là trừ những phần tử đã lọc ra).

Có lẽ bạn đã nghĩ rằng nó thực hiện lọc trước, sau đó chuyển kết quả vào một Bộ sưu tập tùy ý, không có thứ tự của một số dạng - nó không phải vậy.

Vì vậy, nếu bạn sử dụng đầu ra Collections2.filter()làm đầu vào cho một danh sách mới, thì đơn đặt hàng ban đầu của bạn sẽ được giữ lại.

Sử dụng nhập tĩnh (và Lists.newArrayList hàm), nó trở nên khá ngắn gọn:

List filteredList = newArrayList(filter(originalList, predicate));

Lưu ý rằng mặc dù Collections2.filtersẽ không háo hức lặp lại tập hợp bên dưới, nhưng Lists.newArrayList sẽ - nó sẽ trích xuất tất cả các phần tử của tập hợp đã lọc và sao chép chúng vào một tập hợp mới ArrayList.


Nó giống như: List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));hoặc tức là. List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Xaerxess

@Xaerxess: Rất tiếc, vâng, quên vị ... cố định
skaffman

@Bozho: Cảm ơn ... tôi đã mất đủ thời gian :)
skaffman

12

Như Jon đã đề cập, bạn có thể sử dụng Iterables.filter(..)hoặc Collections2.filter(..)và nếu bạn không cần xem trực tiếp, bạn có thể sử dụng ImmutableList.copyOf(Iterables.filter(..))hoặc Lists.newArrayList( Iterables.filter(..))và có đặt hàng sẽ được duy trì.

Nếu bạn thực sự quan tâm đến phần lý do tại sao , bạn có thể truy cập https://github.com/google/guava/issues/505 để biết thêm chi tiết.


6

Tổng hợp những gì những người khác đã nói, bạn có thể dễ dàng tạo một trình bao bọc chung để lọc danh sách:

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.