Cách dễ dàng để chuyển đổi Iterable thành Collection


423

Trong ứng dụng của mình, tôi sử dụng thư viện của bên thứ 3 (Chính xác là Spring Data cho MongoDB).

Các phương thức của thư viện này trả về Iterable<T>, trong khi phần còn lại của mã của tôi mong đợi Collection<T>.

Có phương pháp tiện ích nào ở đâu đó sẽ cho phép tôi nhanh chóng chuyển đổi cái này sang cái khác không? Tôi muốn tránh tạo ra một loạt các foreachvòng lặp trong mã của tôi cho một điều đơn giản như vậy.


3
Bất kỳ phương pháp sử dụng nào để thực hiện thao tác đều bị ràng buộc lặp lại bộ sưu tập, vì vậy bạn không thể mong đợi bất kỳ hiệu suất nào. Nhưng nếu bạn chỉ tìm kiếm đường cú pháp, tôi sẽ chọn Guava hoặc có lẽ là Bộ sưu tập Apache.
Sebastian Ganslandt

"Dù sao cũng bị ràng buộc lặp lại bộ sưu tập ", - không, không phải vậy. Xem câu trả lời của tôi để biết chi tiết.
aioobe

3
trong usecase cụ thể của bạn, bạn chỉ có thể mở rộng CrudRep repository bằng giao diện của riêng bạn với các phương thức trả về Bộ sưu tập <T> / List <T> / Set <T> (nếu cần) thay vì Iterable <T>
Kevin Van Dyck

Câu trả lời:


387

Với Guava, bạn có thể sử dụng Lists.newArrayList (Iterable) hoặc Sets.newHashset (Iterable) , trong số các phương thức tương tự khác. Điều này tất nhiên sẽ sao chép tất cả các yếu tố vào bộ nhớ. Nếu điều đó không được chấp nhận, tôi nghĩ rằng mã của bạn hoạt động với những điều này nên lấy Iterablehơn là Collection. Quả ổi cũng tình cờ cung cấp các phương pháp thuận tiện để thực hiện những việc bạn có thể làm khi Collectionsử dụng Iterable(chẳng hạn như Iterables.isEmpty(Iterable)hoặc Iterables.contains(Iterable, Object)), nhưng ý nghĩa về hiệu suất thì rõ ràng hơn.


1
Nó lặp đi lặp lại qua tất cả các yếu tố trực tiếp? Tức là, Lists.newArrayList(Iterable).clear()một hoạt động thời gian tuyến tính hoặc liên tục?
aioobe

2
@aioobe: Nó tạo ra một bản sao của iterable. Nó đã không xác định rằng một chế độ xem là mong muốn và do hầu hết các phương pháp trên Collectionkhông thể được thực hiện để xem Iterablehoặc không hiệu quả, tôi không có ý nghĩa gì khi làm điều đó.
ColinD

@ColinD nếu tôi muốn xem thì sao? Trên thực tế, điều tôi muốn là một khung nhìn Bộ sưu tập là kết quả của việc nối thêm Bộ sưu tập nguồn với một yếu tố khác. Tôi có thể sử dụng Iterables.concat()nhưng điều đó mang lại Iterable, không phải là Collection:(
Hendy

1
Đây là câu hỏi của tôi: stackoverflow.com/questions/4896662/ . Thật không may, câu trả lời đơn giản không giải quyết được vấn đề là sử dụng Iterables.concat(). Câu trả lời dài hơn cho Collection... Tôi tự hỏi tại sao điều này không được hỗ trợ phổ biến hơn?
Hendy

365

Trong JDK 8+, không sử dụng bất kỳ libs bổ sung nào:

Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);

Chỉnh sửa: Một ở trên là cho Iterator. Nếu bạn đang làm việc với Iterable,

iterable.forEach(target::add);

86
Hoặciterable.forEach(target::add);
Cephalepad

92

Bạn cũng có thể viết phương thức tiện ích của riêng bạn cho việc này:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}

33
+1 Nếu đi từ Iterableđến Collectionlà mối quan tâm duy nhất, tôi thích cách tiếp cận này hơn là nhập thư viện bộ sưu tập của bên thứ 3 lớn.
aioobe

2
4 dòng mã chức năng được ưu tiên hơn nhiều so với 2 MB mã thư viện được biên dịch mà 99% trong số đó không được sử dụng. Có một chi phí khác: phức tạp cấp phép. Giấy phép Apache 2.0 rất linh hoạt, nhưng không phải không có một số nhiệm vụ tẻ nhạt. Lý tưởng nhất là chúng ta sẽ thấy một số các mẫu phổ biến này được tích hợp trực tiếp vào các thư viện thời gian chạy Java.
Jonathan Neufeld

2
Một điểm nữa, vì dù sao bạn cũng đang sử dụng ArrayList, tại sao không chỉ đơn giản là đi với loại Danh sách covariant thay thế? Điều này cho phép bạn đáp ứng nhiều hợp đồng hơn mà không cần truyền tải xuống hoặc biên dịch lại và Java không hỗ trợ các giới hạn loại thấp hơn.
Jonathan Neufeld

@JonathanNeufeld hoặc tại sao không tiếp tục và trả về một ArrayList <T>?
Juan

5
@Juan Bởi vì đó không phải là rất RẮN . Một ArrayList hiển thị các chi tiết triển khai rất có thể không cần thiết (YAGNI), vi phạm các nguyên tắc đảo ngược trách nhiệm và phụ thuộc duy nhất. Tôi sẽ để nó ở Danh sách vì nó lộ ra nhiều hơn một chút so với Bộ sưu tập trong khi vẫn hoàn toàn RẮN. Nếu bạn lo lắng về tác động hiệu năng JVM của opcode INVOKEINTERFACE so với INVOKEVIRTUAL, rất nhiều điểm chuẩn sẽ tiết lộ rằng không đáng để mất ngủ.
Jonathan Neufeld

80

Giải pháp ngắn gọn với Java 8 bằng cách sử dụng java.util.stream:

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}

1
Cách tiếp cận này quá chậm so với IteratorUtilstừcommons-collections
Alex Burdusel

3
Chậm hơn bao nhiêu? IteratorUtils.toList()sử dụng trình lặp theo kiểu Java 5 trước để thêm từng phần tử vào danh sách mới được tạo. Đơn giản và có thể nhanh nhất, nhưng thêm 734 kB vào nhị phân của bạn và bạn có thể tự làm điều này nếu bạn thấy phương pháp này là tốt nhất.
xehpuk

8
Tôi đã thực hiện một điểm chuẩn nguyên thủy kết luận rằng đôi khi cái đầu tiên nhanh hơn, đôi khi cái thứ hai nhanh hơn. Cho chúng tôi thấy điểm chuẩn của bạn.
xehpuk

câu hỏi này có thể là câu trả lời mới được chấp nhận - Thật tuyệt khi tránh những lib quá mức (như Guava).
java-nghiện602

48

IteratorUtilstừ commons-collectionscó thể giúp đỡ (mặc dù họ không hỗ trợ thuốc generic trong phiên bản ổn định mới nhất 3.2.1):

@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());

Phiên bản 4.0 (trong SNAPSHOT tại thời điểm này) hỗ trợ thuốc generic và bạn có thể thoát khỏi @SuppressWarnings.

Cập nhật: Kiểm tra IterableAsListtừ Cactoos .


5
Nhưng điều đó đòi hỏi một Iterator, không phải Iterable
hithwen 23/1/2015

5
@hithwen, tôi không hiểu - Iterable cung cấp Iterator (như chi tiết trong câu trả lời) - vấn đề là gì?
Tom

Không biết tôi đang nghĩ gì ^^ U
hithwen 29/2/2016

2
Vì 4.1 cũng có IterableUtils.toList(Iterable), đây là một phương pháp tiện lợi và được sử dụng IteratorUtilsdưới mui xe, nhưng cũng không an toàn (không giống như IteratorUtils.toList).
Yoory N.

21

Từ CollectionUtils :

List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())

Dưới đây là các nguồn đầy đủ của phương pháp tiện ích này:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}

Nó lặp đi lặp lại qua tất cả các yếu tố trực tiếp? Tức là, Lists.newArrayList(someIterable).clear()một hoạt động thời gian tuyến tính hoặc liên tục?
aioobe

Tôi đã thêm mã nguồn của addAll, như tên ngụ ý, nó sao chép các giá trị lặp lại lần lượt; nó tạo ra một bản sao chứ không phải là một khung nhìn
Tomasz Nurkiewicz

Thật đáng tiếc khi không có phương pháp nào CollectionUtilsđể bỏ qua việc tạo ra bộ sưu tập trong một dòng bổ sung.
Karl Richter

Liên kết bị hỏng ☝️☝️
Hola Soy Edu Feliz Navidad

14

Trong khi ở đó, đừng quên rằng tất cả các bộ sưu tập là hữu hạn, trong khi Iterable không có bất kỳ lời hứa nào. Nếu một cái gì đó là Iterable, bạn có thể lấy Iterator và đó là nó.

for (piece : sthIterable){
..........
}

sẽ được mở rộng thành:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}

it.hasNext () không bắt buộc phải trả về false. Do đó, trong trường hợp chung, bạn không thể mong đợi có thể chuyển đổi mọi Iterable thành Collection. Ví dụ: bạn có thể lặp lại tất cả các số tự nhiên dương, lặp đi lặp lại trên một cái gì đó có chu kỳ trong đó tạo ra kết quả tương tự lặp đi lặp lại, v.v.

Mặt khác: câu trả lời của Atrey khá ổn.


1
Có ai từng thực sự bắt gặp một Iterable lặp đi lặp lại một cái gì đó vô hạn (như ví dụ về số tự nhiên được đưa ra trong câu trả lời), trong thực tế / mã thực? Tôi sẽ nghĩ rằng một Iterable như vậy sẽ gây ra đau đớn và đau khổ ở rất nhiều nơi ... :)
David

2
@David Mặc dù tôi không thể chỉ ra một Iterator vô hạn trong bất kỳ mã sản xuất nào của mình, tôi có thể nghĩ về các trường hợp chúng có thể xảy ra. Một trò chơi video có thể có một kỹ năng tạo ra các vật phẩm theo mô hình chu kỳ mà câu trả lời ở trên gợi ý. Trong khi tôi chưa gặp phải bất kỳ trình lặp vô hạn nào, tôi chắc chắn đã gặp các trình vòng lặp trong đó bộ nhớ là mối quan tâm thực sự. Tôi có các vòng lặp trên các tập tin trên đĩa. Nếu tôi có đĩa 1TB đầy đủ và 4GB ram, tôi có thể dễ dàng hết bộ nhớ khi chuyển đổi trình lặp của mình thành Bộ sưu tập.
triệt để 101

14

Tôi sử dụng FluentIterable.from(myIterable).toList()rất nhiều.


9
Cần lưu ý rằng đó là từ ổi quá.
Vadzim

Hoặc từ org.apache.commons.collections4. Sau đó, đó là FluentIterable.of (myIterable) .toList ()
du-it

9

Đây không phải là một câu trả lời cho câu hỏi của bạn nhưng tôi tin rằng nó là giải pháp cho vấn đề của bạn. Giao diện org.springframework.data.repository.CrudRepositorythực sự có các phương thức trả về java.lang.Iterablenhưng bạn không nên sử dụng giao diện này. Thay vào đó sử dụng giao diện phụ, trong trường hợp của bạn org.springframework.data.mongodb.repository.MongoRepository. Giao diện này có các phương thức trả về các đối tượng thuộc loại java.util.List.


2
Tôi sẽ quảng bá bằng cách sử dụng CrudRep repository chung để tránh ràng buộc mã của bạn với việc triển khai cụ thể.
stanlick

7

Tôi sử dụng tiện ích tùy chỉnh của mình để bỏ Bộ sưu tập hiện có nếu có.

Chủ yếu:

public static <T> Collection<T> toCollection(Iterable<T> iterable) {
    if (iterable instanceof Collection) {
        return (Collection<T>) iterable;
    } else {
        return Lists.newArrayList(iterable);
    }
}

Lý tưởng nhất là ở trên sẽ sử dụng ImmutableList, nhưng ImmutableCollection không cho phép null có thể cung cấp kết quả không mong muốn.

Các xét nghiệm:

@Test
public void testToCollectionAlreadyCollection() {
    ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST);
    assertSame("no need to change, just cast", list, toCollection(list));
}

@Test
public void testIterableToCollection() {
    final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST);

    Collection<String> collection = toCollection(new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return expected.iterator();
        }
    });
    assertNotSame("a new list must have been created", expected, collection);
    assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection));
}

Tôi triển khai các tiện ích tương tự cho tất cả các kiểu con của Bộ sưu tập (Bộ, Danh sách, v.v.). Tôi nghĩ những thứ này đã là một phần của ổi, nhưng tôi không tìm thấy nó.


1
Câu trả lời cũ của bạn là cơ sở của một câu hỏi stackoverflow.com/questions/32570534/ cho thu hút rất nhiều lượt xem và bình luận.
Paul Boddington

6

Ngay sau khi bạn gọi contains, containsAll, equals, hashCode, remove, retainAll, sizehay toArray, bạn sẽ phải đi qua các yếu tố nào.

Nếu bạn thỉnh thoảng chỉ gọi các phương thức như isEmptyhoặc cleartôi cho rằng bạn sẽ tốt hơn bằng cách tạo ra bộ sưu tập một cách lười biếng. Ví dụ, bạn có thể có một bản sao lưu ArrayListđể lưu trữ các phần tử được lặp trước đó.

Tôi không biết về bất kỳ lớp nào như vậy trong bất kỳ thư viện nào, nhưng nó nên là một bài tập khá đơn giản để viết lên.



6

Trong Java 8, bạn có thể làm điều này để thêm tất cả các phần tử từ một Iterableđến Collectionvà trả lại nó:

public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
  Collection<T> collection = new ArrayList<>();
  iterable.forEach(collection::add);
  return collection;
}

Lấy cảm hứng từ câu trả lời @Afreys.


5

Vì RxJava là một cái búa và loại này trông giống như một cái đinh, bạn có thể làm

Observable.from(iterable).toList().toBlocking().single();

23
Có cách nào để có được jquery tham gia có lẽ?
Dmitry Minkovsky

3
nó gặp sự cố nếu có mục null trong RxJava. phải không
MBH

Tôi tin rằng RxJava2 không cho phép các mục null, sẽ ổn trong RxJava.
DariusL

4

Đây là một SSCCE cho một cách tuyệt vời để làm điều này trong Java 8

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class IterableToCollection {
    public interface CollectionFactory <T, U extends Collection<T>> {
        U createCollection();
    }

    public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) {
        U collection = factory.createCollection();
        iterable.forEach(collection::add);
        return collection;
    }

    public static void main(String[] args) {
        Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
        ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
        HashSet<Integer> hashSet = collect(iterable, HashSet::new);
        LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
    }
}

2

Hai nhận xét

  1. Không cần phải chuyển đổi Iterable thành Collection để sử dụng vòng lặp foreach - Iterable có thể được sử dụng trực tiếp trong vòng lặp đó, không có sự khác biệt về cú pháp, vì vậy tôi hầu như không hiểu tại sao câu hỏi ban đầu được hỏi.
  2. Cách được đề xuất để chuyển đổi Iterable thành Collection là không an toàn (tương tự liên quan đến CollectionUtils) - không có gì đảm bảo rằng các cuộc gọi tiếp theo đến phương thức next () trả về các thể hiện đối tượng khác nhau. Hơn nữa, mối quan tâm này không phải là lý thuyết thuần túy. Ví dụ: Việc thực hiện lặp lại được sử dụng để truyền các giá trị cho phương thức rút gọn của Hadoop Reducer luôn trả về cùng một thể hiện giá trị, chỉ với các giá trị trường khác nhau. Vì vậy, nếu bạn áp dụng makeCollection từ phía trên (hoặc CollectionUtils.add ALL (Iterator)), bạn sẽ kết thúc với một bộ sưu tập với tất cả các yếu tố giống hệt nhau.


1

Tôi đã không thấy một giải pháp một dòng đơn giản mà không có bất kỳ sự phụ thuộc nào. Tôi sử dụng đơn giản

List<Users> list;
Iterable<IterableUsers> users = getUsers();

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());

1

Tôi đã xem qua một tình huống tương tự khi cố gắng lấy một Listcủa Projects, chứ không phải là mặc định Iterable<T> findAll()khai báo trong CrudRepositorygiao diện. Vì vậy, trong ProjectRepositorygiao diện của tôi (kéo dài từ CrudRepository), tôi chỉ đơn giản khai báo findAll()phương thức để trả về một List<Project>thay vì Iterable<Project>.

package com.example.projectmanagement.dao;

import com.example.projectmanagement.entities.Project;
import org.springframework.data.repository.CrudRepository;
import java.util.List;

public interface ProjectRepository extends CrudRepository<Project, Long> {

    @Override
    List<Project> findAll();
}

Đây là giải pháp đơn giản nhất, tôi nghĩ, không yêu cầu logic chuyển đổi hoặc sử dụng các thư viện bên ngoài.


0

Bạn có thể sử dụng các nhà máy Bộ sưu tập Eclipse :

Iterable<String> iterable = Arrays.asList("1", "2", "3");

MutableList<String> list = Lists.mutable.withAll(iterable);
MutableSet<String> set = Sets.mutable.withAll(iterable);
MutableSortedSet<String> sortedSet = SortedSets.mutable.withAll(iterable);
MutableBag<String> bag = Bags.mutable.withAll(iterable);
MutableSortedBag<String> sortedBag = SortedBags.mutable.withAll(iterable);

Bạn cũng có thể chuyển đổi Iterablesang a LazyIterablevà sử dụng các phương thức chuyển đổi hoặc bất kỳ API nào có sẵn khác.

Iterable<String> iterable = Arrays.asList("1", "2", "3");
LazyIterable<String> lazy = LazyIterate.adapt(iterable);

MutableList<String> list = lazy.toList();
MutableSet<String> set = lazy.toSet();
MutableSortedSet<String> sortedSet = lazy.toSortedSet();
MutableBag<String> bag = lazy.toBag();
MutableSortedBag<String> sortedBag = lazy.toSortedBag();

Tất cả các Mutableloại trên mở rộng java.util.Collection.

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

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.