Chuyển đổi mảng Java thành Iterable


150

Tôi có một mảng nguyên thủy, ví dụ cho int, int [] foo. Nó có thể là một kích thước nhỏ, hoặc không.

int foo[] = {1,2,3,4,5,6,7,8,9,0};

Cách tốt nhất để tạo ra một Iterable<Integer>từ nó là gì?

Iterable<Integer> fooBar = convert(foo);

Ghi chú:

Vui lòng không trả lời bằng cách sử dụng các vòng lặp (trừ khi bạn có thể đưa ra lời giải thích tốt về cách trình biên dịch làm điều gì đó thông minh về chúng?)

Cũng lưu ý rằng

int a[] = {1,2,3};
List<Integer> l = Arrays.asList(a);

Thậm chí sẽ không biên dịch

Type mismatch: cannot convert from List<int[]> to List<Integer>

Ngoài ra kiểm tra Tại sao một mảng không thể gán cho Iterable? trước khi trả lời.

Ngoài ra, nếu bạn sử dụng một số thư viện (ví dụ, ổi), vui lòng giải thích lý do tại sao đây là tốt nhất. (Bởi vì từ Google không phải là câu trả lời hoàn chỉnh: P)

Cuối cùng, vì dường như có một bài tập về nhà về điều đó, tránh đăng mã bài tập về nhà.


bản sao có thể có của Iterator cho mảng
NPE

Thêm chúng vào LinkedList sau đó chỉ cần trả về iterator của Set đó.

Câu trả lời:


117
Integer foo[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

List<Integer> list = Arrays.asList(foo);
// or
Iterable<Integer> iterable = Arrays.asList(foo);

Mặc dù bạn cần sử dụng một Integermảng (không phải là một intmảng) để làm việc này.

Đối với người nguyên thủy, bạn có thể sử dụng ổi:

Iterable<Integer> fooBar = Ints.asList(foo);
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>15.0</version>
    <type>jar</type>
</dependency>

Dành cho Java8: (từ câu trả lời của Jin Kwon)

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();

10
Hai lưu ý: 1) anh ta có int, không phải Integer2) Listđã có Iterablenên dòng thứ ba là vô nghĩa.
maksimov

1
anh ta cần Iterable rằng tại sao có dòng thứ ba.
fmucar

5
Dòng thứ 2 và 3 là các tùy chọn tôi sẽ nói :)
fmucar

1
Đây không phải là một phần của bài tập về nhà, tôi chỉ cố gắng tránh sao chép mã cho hàm gỡ lỗi xử lý nội dung của một mảng hoặc danh sách ... Trong khi nhìn xung quanh tôi thực sự đã tìm thấy Arrays.asList (..);, nhưng ít nhất Eclipse dường như nghĩ rằng nó sẽ không làm những gì tôi muốn (ví dụ: Nó xâm nhập vào kết quả của Arrays.asList (foo) dưới dạng Danh sách <int []>, chứ không phải Danh sách <Integer> ...) Tôi thấy điều này đủ thú vị cho một câu hỏi ... (-Breaking bình luận trong các phần gây ra các limits-)
NTG

1
Nói chung, người ta có thể nghĩ ra rất nhiều cách để làm điều đó, nhưng tôi đã tự hỏi điều gì là TỐT NHẤT (ví dụ như một vòng lặp sẽ imho chậm hơn so với ... {tốt, vấn đề là tôi không thể nghĩ ra bất cứ điều gì!: )}) Cũng kiểm tra stackoverflow.com/questions/1160081/ vì một cuộc thảo luận về lý do tại sao, câu hỏi của tôi không phải là tại sao, nhưng làm thế nào và loại container nào là tốt nhất (tại sao ArrayList? Thực tế, tôi có thể tưởng tượng ra một số Tóm tắt trình bao bọc sử dụng Generics .., Có lẽ phụ thuộc vào kích thước ...)
ntg

44

chỉ 2 xu của tôi:

final int a[] = {1,2,3};

java.lang.Iterable<Integer> aIterable=new Iterable<Integer>() {

    public Iterator<Integer> iterator() {
       return new Iterator<Integer>() {
            private int pos=0;

            public boolean hasNext() {
               return a.length>pos;
            }

            public Integer next() {
               return a[pos++];
            }

            public void remove() {
                throw new UnsupportedOperationException("Cannot remove an element of an array.");
            }
        };
    }
};

9
remove () không cần thiết trong java 8, vì đây là phương thức mặc định ném UnsupportedOperationException. Chỉ khi bạn muốn cung cấp một thông điệp giải thích tốt hơn.
Alex

+1 Tôi làm một cái gì đó tương tự để tạo một Iterator<Character>từ a String. Việc thực hiện theo cách riêng của bạn Iteratorcó vẻ như là cách duy nhất để tránh lặp lại một cách không cần thiết thông qua tất cả các giá trị để chuyển đổi từ loại đối tượng sang loại nguyên thủy ( Ints.asList()ví dụ như thông qua Guava ), để có thể lấy Iteratortừ một Listthứ được tạo ra.
spaaarky21

2
Bạn nói đúng Alex. Các phương thức mặc định đã được thêm vào Java 8. Năm 2013 tôi đã thêm đoạn mã cổ này vào đây.
Joerg Ruethschilling

29

Với Java 8, bạn có thể làm điều này.

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();

20

Guava cung cấp bộ điều hợp bạn muốn dưới dạng Int.asList () . Có một loại tương đương cho mỗi loại nguyên thủy trong lớp liên kết, ví dụ, Booleanscho boolean, v.v.

int foo[] = {1,2,3,4,5,6,7,8,9,0};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
    System.out.println(i);
}

Các đề xuất ở trên Arrays.asListsẽ không hiệu quả, ngay cả khi chúng được biên dịch vì bạn nhận được Iterator<int[]>thay vì Iterator<Integer>. Điều gì xảy ra là thay vì tạo một danh sách được hỗ trợ bởi mảng của bạn, bạn đã tạo một danh sách 1 phần tử của mảng, có chứa mảng của bạn.


chỉ là một lưu ý: liên kết không hoạt động nữa. Liên kết Github: github.com/google/guava/blob/master/guava/src/com/google/common/
mẹo

Cảm ơn @Passi, đã sửa (dường như không thể tìm thấy cách nào được Google hỗ trợ để liên kết với javadoc nữa nên tôi đã liên kết với nguồn bạn cung cấp).
BeeOnRope

8

Tôi đã có cùng một vấn đề và giải quyết nó như thế này:

final YourType[] yourArray = ...;
return new Iterable<YourType>() {
  public Iterator<YourType> iterator() {
     return Iterators.forArray(yourArray);   // Iterators is a Google guava utility
  }
}

Bản thân iterator là một kẻ lười biếng UnmodifiableIteratornhưng đó chính xác là những gì tôi cần.


7

Trong Java 8 trở lên, Iterablelà một giao diện chức năng trả về Iterator. Vì vậy, bạn có thể làm điều này.

int[] array = {1, 2, 3};
Iterable<Integer> iterable = () -> Arrays.stream(array).iterator();
for (int i : iterable)
    System.out.println(i);

->

1
2
3

3

Trước hết, tôi chỉ có thể đồng ý rằng Arrays.asList(T...)rõ ràng đó là giải pháp tốt nhất cho các kiểu hoặc mảng Wrapper với các kiểu dữ liệu không nguyên thủy. Phương thức này gọi một hàm tạo của một AbstractListtriển khai tĩnh riêng đơn giản trong Arrayslớp, về cơ bản lưu tham chiếu mảng đã cho dưới dạng trường và mô phỏng danh sách bằng cách ghi đè các phương thức cần thiết.

Nếu bạn có thể chọn giữa loại nguyên thủy hoặc loại Wrapper cho mảng của mình, tôi sẽ sử dụng loại Wrapper cho các tình huống như vậy nhưng tất nhiên, nó không phải lúc nào cũng hữu ích hoặc bắt buộc. Sẽ chỉ có hai khả năng bạn có thể làm:

1) Bạn có thể tạo một lớp với một phương thức tĩnh cho mỗi mảng kiểu dữ liệu nguyên thủy ( boolean, byte, short, int, long, char, float, doubletrả về một Iterable<WrapperType >. Các phương thức này sẽ sử dụng các lớp ẩn danh của Iterator(bên cạnh đóIterable) được phép chứa tham chiếu của đối số của phương thức bao gồm (ví dụ an int[]) dưới dạng trường để triển khai các phương thức.

-> Cách tiếp cận này là hiệu quả và giúp bạn tiết kiệm bộ nhớ (ngoại trừ bộ nhớ của các phương thức mới được tạo, mặc dù, việc sử dụng Arrays.asList()sẽ lấy bộ nhớ theo cùng một cách)

2) Vì các mảng không có phương thức (như được đọc ở bên cạnh bạn đã liên kết) họ cũng không thể cung cấp một Iteratorví dụ. Nếu bạn thực sự quá lười biếng để viết các lớp mới, bạn phải sử dụng một thể hiện của một lớp đã có sẵn để thực hiện Iterablebởi vì không có cách nào khác ngoài việc khởi tạo Iterablehoặc một kiểu con.
Cách DUY NHẤT để tạo triển khai phái sinh Bộ sưu tập hiện cóIterablelà sử dụng một vòng lặp (ngoại trừ bạn sử dụng các lớp ẩn danh như được mô tả ở trên) hoặc bạn khởi tạo một Iterablelớp triển khai có hàm tạo cho phép một Object[]mảng kiểu nguyên thủy (vì không cho phép các mảng có các phần tử kiểu nguyên thủy) nhưng theo như tôi biết, API Java không có một lớp học như thế.

Lý do của vòng lặp có thể được giải thích dễ dàng:
đối với mỗi Bộ sưu tập, bạn cần các đối tượng và kiểu dữ liệu nguyên thủy không phải là đối tượng. Các đối tượng lớn hơn nhiều so với các kiểu nguyên thủy để chúng yêu cầu dữ liệu bổ sung phải được tạo cho từng phần tử của mảng kiểu nguyên thủy. Điều đó có nghĩa là nếu hai cách của ba (sử dụng Arrays.asList(T...)hoặc sử dụng Bộ sưu tập hiện tại) yêu cầu tổng hợp các đối tượng, bạn cần tạo cho mỗi giá trị nguyên thủy củaint[]mảng đối tượng bao bọc. Cách thứ ba sẽ sử dụng mảng như hiện tại và sử dụng nó trong một lớp ẩn danh vì tôi nghĩ nó thích hợp hơn do hiệu suất nhanh.

Ngoài ra còn có một chiến lược thứ ba sử dụng một Objectđối số làm phương thức cho phương thức mà bạn muốn sử dụng mảng hoặc Iterablenó sẽ yêu cầu kiểm tra kiểu để tìm ra loại đối số nào, tuy nhiên tôi sẽ không đề xuất nó như bạn thường cần xem xét rằng Đối tượng không phải luôn luôn là loại được yêu cầu và bạn cần mã riêng cho các trường hợp nhất định.

Tóm lại, đó là lỗi của hệ thống Kiểu chung có vấn đề của Java, không cho phép sử dụng các kiểu nguyên thủy làm kiểu chung sẽ tiết kiệm rất nhiều mã bằng cách sử dụng đơn giảnArrays.asList(T...). Vì vậy, bạn cần lập trình cho từng mảng kiểu nguyên thủy, bạn cần một phương thức như vậy (về cơ bản không có sự khác biệt nào đối với bộ nhớ được sử dụng bởi chương trình C ++ sẽ tạo cho mỗi đối số kiểu được sử dụng một phương thức riêng biệt.


3

Bạn có thể sử dụng IterableOftừ Cactoos :

Iterable<String> names = new IterableOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);

Sau đó, bạn có thể biến nó thành một danh sách bằng cách sử dụng ListOf:

List<String> names = new ListOf<>(
  new IterableOf<>(
    "Scott Fitzgerald", "Fyodor Dostoyevsky"
  )
);

Hoặc đơn giản là thế này:

List<String> names = new ListOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);

1

Trong khi một câu trả lời tương tự đã được đăng, tôi nghĩ lý do để sử dụng PrimitiveIterator.OfInt mới không rõ ràng. Một giải pháp tốt là sử dụng Java 8 PrimitiveIterator vì nó chuyên dùng cho các kiểu int nguyên thủy (và tránh hình phạt đấm bốc / bỏ hộp bổ sung):

    int[] arr = {1,2,3};
    // If you use Iterator<Integer> here as type then you can't get the actual benefit of being able to use nextInt() later
    PrimitiveIterator.OfInt iterator = Arrays.stream(arr).iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.nextInt());
        // Use nextInt() instead of next() here to avoid extra boxing penalty
    }

Tham chiếu: https://doc.bccnsoft.com/docs/jdk8u12-docs/api/java/util/PrimitiveIterator.OfInt.html


-2

Trong java8 IntSteam, luồng có thể được đóng hộp cho luồng Số nguyên.

public static Iterable<Integer> toIterable(int[] ints) {
    return IntStream.of(ints).boxed().collect(Collectors.toList());
}

Tôi nghĩ vấn đề hiệu suất dựa trên kích thước của mảng.

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.