Lấy danh sách từ java.util.stream.Stream trong Java 8


443

Tôi đã chơi xung quanh với lambdas Java 8 để dễ dàng lọc các bộ sưu tập. Nhưng tôi đã không tìm thấy một cách ngắn gọn để lấy kết quả dưới dạng một danh sách mới trong cùng một tuyên bố. Đây là cách tiếp cận ngắn gọn nhất của tôi cho đến nay:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = new ArrayList<>();
sourceLongList.stream().filter(l -> l > 100).forEach(targetLongList::add);

Các ví dụ trên mạng không trả lời câu hỏi của tôi vì chúng dừng lại mà không tạo danh sách kết quả mới. Phải có một cách ngắn gọn hơn. Tôi đã dự đoán rằng Streamlớp có các phương thức toList(), toSet(),,

Có cách nào để các biến targetLongListcó thể được chỉ định trực tiếp bởi dòng thứ ba không?


7
Trong trường hợp bạn không cần sourceLongListsau đó Collection.removeIf(…)để thuận tiện.
Holger

2
Còn cái này thì sao? List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());
leeyuiwah

Câu trả lời:


609

Những gì bạn đang làm có thể là cách đơn giản nhất, miễn là luồng của bạn duy trì tuần tự, nếu không bạn sẽ phải thực hiện cuộc gọi đến tuần tự () trước đó forEach.

[chỉnh sửa sau: lý do cuộc gọi đến tuần tự () là cần thiết là mã khi nó đứng ( forEach(targetLongList::add)) sẽ không phù hợp nếu luồng phát song song. Ngay cả khi đó, nó sẽ không đạt được hiệu quả như mong muốn, vì forEachrõ ràng là không rõ ràng, ngay cả trong một chuỗi tuần tự, thứ tự xử lý phần tử không được đảm bảo. Bạn sẽ phải sử dụng forEachOrderedđể đảm bảo đặt hàng chính xác. Ý định của các nhà thiết kế API Stream là bạn sẽ sử dụng trình thu thập trong tình huống này, như dưới đây.]

Một thay thế là

targetLongList = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(Collectors.toList());

10
Bổ sung: Tôi nghĩ rằng mã này sẽ ngắn hơn một chút, rõ ràng và đẹp hơn nếu bạn sử dụng nhập khẩu tĩnh toList. Điều này được thực hiện bằng cách đặt các mục sau trong số các tệp nhập : static import java.util.stream.Collectors.toList;. Sau đó, cuộc gọi thu thập chỉ đọc .collect(toList()).
Lii

4
Trong Eclipse, có thể làm cho IDE thêm một nhập tĩnh cho các phương thức. Điều này được thực hiện bằng cách thêm Collectorslớp trong Tùy chọn -> Java -> Trình chỉnh sửa -> Hỗ trợ nội dung -> Mục ưa thích . Sau này, bạn chỉ phải gõ toLitại hit Ctr + Space để điền vào IDE toListvà thêm nhập tĩnh.
Lii

3
Một điều cần lưu ý là IntStreamvà một số khác gần như Streamkhông hoàn toàn không có collect(Collector)phương pháp và bạn sẽ phải gọi IntStream.boxed()để chuyển đổi chúng thành thông thường Streamtrước. Sau đó, một lần nữa, có thể bạn chỉ muốn toArray().
Mutant Bob

tại sao chúng ta phải sử dụng sequential()trước forEachhoặc sử dụng 'forEachOrdered`
amarnath

1
@amarnathharish Bởi vì foreach không đảm bảo trình tự thực hiện hoạt động cho một dòng song song. JavaDoc nói "Hành vi của hoạt động này rõ ràng là không có tính xác định. Đối với các đường ống dòng song song, hoạt động này không đảm bảo tôn trọng thứ tự gặp gỡ của luồng, vì làm như vậy sẽ hy sinh lợi ích của việc xử lý song song." (Câu đầu tiên của câu nói này thực sự có nghĩa là trật tự mà không được bảo đảm cho dòng tuần tự, hoặc, mặc dù trong thực tế nó được bảo tồn.)
Maurice Naftalin

183

Cập nhật:

Một cách tiếp cận khác là sử dụng Collectors.toList:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toList());

Giải pháp theo thời gian:

Một cách tiếp cận khác là sử dụng Collectors.toCollection:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toCollection(ArrayList::new));

60
Tuy nhiên, điều này hữu ích nếu bạn muốn triển khai Danh sách cụ thể.
orbfish

1
Mặc dù được khuyến nghị để mã hóa các giao diện, nhưng có một số trường hợp rõ ràng (một trong số đó là GWT) khi bạn phải mã hóa các triển khai cụ thể (trừ khi bạn muốn tất cả các triển khai Danh sách được biên dịch và phân phối dưới dạng javascript).
Eduard Korenschi

3
Một pro khác cho phương pháp này, từ Collectors::toListjavadoc: "Không có sự đảm bảo nào về loại, tính biến đổi, tính tuần tự hoặc tính an toàn của danh sách được trả về; nếu cần thêm quyền kiểm soát Danh sách được trả về, hãy sử dụng toCollection(Supplier)."
Charles Wood

12

Tôi thích sử dụng một phương thức tiện dụng trả về một bộ sưu tập ArrayListkhi đó là những gì tôi muốn.

Tôi nghĩ rằng giải pháp sử dụng Collectors.toCollection(ArrayList::new)là quá ồn ào cho một hoạt động phổ biến như vậy.

Thí dụ:

ArrayList<Long> result = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

Với câu trả lời này, tôi cũng muốn chứng minh rằng việc tạo và sử dụng các bộ sưu tập tùy chỉnh rất đơn giản, thường rất hữu ích.


Nếu bạn khai báo kết quả là Danh sách <Long> bạn không cần sử dụng phương thức sử dụng này. Collector.toList sẽ làm. Ngoài ra, sử dụng các lớp cụ thể thay vì giao diện là một mùi mã.
Lluis Martinez

1
@LluisMartinez: "Collector.toList sẽ làm." : Không, không phải trong nhiều tình huống. Bởi vì đó không phải là một ý tưởng tốt để sử dụng toListnếu bạn muốn sửa đổi danh sách sau này trong chương trình. Các toListtài liệu nói điều này: "Không có bảo đảm về chủng loại, mutability, serializability, hoặc thread-an toàn của Danh sách trở lại, nếu kiểm soát tốt hơn Danh sách trở lại là cần thiết, sử dụng toCollection." . Câu trả lời của tôi cho thấy một cách để làm cho nó thuận tiện hơn để làm điều đó trong một trường hợp phổ biến.
Lii

Nếu bạn muốn đặc biệt tạo một ArrayList thì không sao.
Lluis Martinez

5

Nếu bạn có một mảng các nguyên thủy, bạn có thể sử dụng các bộ sưu tập nguyên thủy có sẵn trong Bộ sưu tập Eclipse .

LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);

Nếu bạn không thể thay đổi sourceLongList từ List:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = 
    ListAdapter.adapt(sourceLongList).select(l -> l > 100, new ArrayList<>());

Nếu bạn muốn sử dụng LongStream:

long[] sourceLongs = new long[]{1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L};
LongList targetList = 
    LongStream.of(sourceLongs)
    .filter(l -> l > 100)
    .collect(LongArrayList::new, LongArrayList::add, LongArrayList::addAll);

Lưu ý: Tôi là người đóng góp cho Bộ sưu tập Eclipse.


4

Một cách hiệu quả hơn một chút (tránh việc tạo Danh sách nguồn và tự động hủy hộp bằng bộ lọc):

List<Long> targetLongList = LongStream.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L)
    .filter(l -> l > 100)
    .boxed()
    .collect(Collectors.toList());


1

Nếu bạn không phiền khi sử dụng các thư viện của bên thứ 3, lib của cyclop- Reac (tiết lộ tôi là người đóng góp) có các phần mở rộng cho tất cả các loại Bộ sưu tập JDK , bao gồm cả Danh sách . Giao diện ListX mở rộng java.util.List và thêm một số lượng lớn các toán tử hữu ích, bao gồm cả bộ lọc.

Bạn chỉ có thể viết-

ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);

ListX cũng có thể được tạo từ Danh sách hiện có (thông qua ListX.fromIterable)


1

Có một biến thể khác của phương thức thu thập được cung cấp bởi lớp LongStream và tương tự bởi các lớp IntStream và DoubleStream.

<R> R collect(Supplier<R> supplier,
              ObjLongConsumer<R> accumulator,
              BiConsumer<R,R> combiner)

Thực hiện một hoạt động giảm đột biến trên các yếu tố của luồng này. Một giảm có thể thay đổi là một trong đó giá trị giảm là một thùng chứa kết quả có thể thay đổi, chẳng hạn như ArrayList và các phần tử được kết hợp bằng cách cập nhật trạng thái của kết quả thay vì thay thế kết quả. Điều này tạo ra một kết quả tương đương với:

R result = supplier.get();
  for (long element : this stream)
       accumulator.accept(result, element);
  return result;

Giống như giảm (dài, LongBinaryOperator), các hoạt động thu thập có thể được song song mà không yêu cầu đồng bộ hóa bổ sung. Đây là một hoạt động thiết bị đầu cuối.

Và trả lời câu hỏi của bạn với phương pháp thu thập này như sau:

    LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, (list, value) -> list.add(value)
    , (list1, list2) -> list1.addAll(list2));

Dưới đây là biến thể tham chiếu phương thức khá thông minh nhưng một số điều khó hiểu:

     LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, List::add , List::addAll);

Dưới đây sẽ là biến thể Hashset:

     LongStream.of(1L, 2L, 3L, 3).filter(i -> i > 2)
     .collect(HashSet::new, HashSet::add, HashSet::addAll);

Tương tự biến thể LinkedList giống như thế này:

     LongStream.of(1L, 2L, 3L, 3L)
     .filter(i -> i > 2)
     .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

0

Trong trường hợp ai đó (như tôi) ngoài kia đang tìm cách đối phó với các Đối tượng thay vì các kiểu nguyên thủy thì hãy sử dụng mapToObj()

String ss = "An alternative way is to insert the following VM option before "
        + "the -vmargs option in the Eclipse shortcut properties(edit the "
        + "field Target inside the Shortcut tab):";

List<Character> ll = ss
                        .chars()
                        .mapToObj(c -> new Character((char) c))
                        .collect(Collectors.toList());

System.out.println("List type: " + ll.getClass());
System.out.println("Elem type: " + ll.get(0).getClass());
ll.stream().limit(50).forEach(System.out::print);

in:

List type: class java.util.ArrayList
Elem type: class java.lang.Character
An alternative way is to insert the following VM o

0
String joined = 
                Stream.of(isRead?"read":"", isFlagged?"flagged":"", isActionRequired?"action":"", isHide?"hide":"")
                      .filter(s -> s != null && !s.isEmpty())
                      .collect(Collectors.joining(","));

0

Đây là mã của AbacusUtil

LongStream.of(1, 10, 50, 80, 100, 120, 133, 333).filter(e -> e > 100).toList();

Tiết lộ tiết lộ Tôi là nhà phát triển của AbacusUtil.


Tôi không tìm thấy bất kỳ phương thức toList nào có trong lớp LongStream. Bạn có thể chạy mã này?
Vaneet Kataria

@VaneetKataria thử com.landawn.abacus.util.stream.LongStreamhoặc LongStreamExtrong AbacusUtil
user_3380739

0

Bạn có thể viết lại mã như dưới đây:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = sourceLongList.stream().filter(l -> l > 100).collect(Collectors.toList());

Cảm ơn vì đầu vào của bạn. Tuy nhiên, vui lòng giải thích những gì bạn đã thay đổi và trong bao xa nó liên quan đến câu hỏi.
B - rian

Trước tiên, tôi đã chuyển đổi ArrayList của mình để hấp chúng bằng bộ lọc Tôi lọc ra dữ liệu cần thiết. Cuối cùng tôi đã sử dụng phương thức thu thập của luồng java 8 để thu thập dữ liệu trong danh sách mới được gọi là targetLongList.
Pratik Pawar

0

Để thu thập trong một danh sách có thể thay đổi:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toList());

Để thu thập trong một danh sách bất biến:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toUnmodifiableList());

Giải thích collecttừ JavaDoc :

Thực hiện thao tác giảm có thể thay đổi trên các phần tử của luồng này bằng Collector. Bộ sưu tập đóng gói các hàm được sử dụng làm đối số để thu thập (Nhà cung cấp, BiConsumer, BiConsumer), cho phép sử dụng lại các chiến lược thu thập và thành phần của các hoạt động thu thập như phân nhóm nhiều cấp hoặc phân vùng. Nếu luồng song song và Collector đồng thời và luồng không được sắp xếp hoặc collector không được sắp xếp, thì việc giảm đồng thời sẽ được thực hiện (xem Collector để biết chi tiết về giảm đồng thời.)

Đây là một hoạt động thiết bị đầu cuối.

Khi được thực hiện song song, nhiều kết quả trung gian có thể được khởi tạo, điền và hợp nhất để duy trì sự cô lập của các cấu trúc dữ liệu có thể thay đổi. Do đó, ngay cả khi được thực thi song song với các cấu trúc dữ liệu không an toàn luồng (như ArrayList), không cần đồng bộ hóa bổ sung để giảm song song.


-3

Nếu bạn không sử dụng parallel()nó sẽ làm việc

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);

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

sourceLongList.stream().peek(i->targetLongList.add(i)).collect(Collectors.toList());

Tôi không thích rằng coll () chỉ được sử dụng để điều khiển luồng sao cho hook peek () được gọi trên mỗi mục. Kết quả của hoạt động thiết bị đầu cuối bị loại bỏ.
Daniel K.

Nó rất kỳ lạ để gọi collectvà sau đó không lưu giá trị trả lại. Trong trường hợp đó bạn có thể sử dụng forEachthay thế. Nhưng đó vẫn là một giải pháp kém.
Lii

1
Sử dụng peek () theo cách này là một antipotype.
Grzegorz Piwowarek

Theo phương pháp xem tài liệu Java Stream phải được sử dụng cho mục đích gỡ lỗi. Nó không nên được sử dụng cho bất kỳ xử lý nào ngoài việc gỡ lỗi.
Vaneet Kataria
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.