Перетворити масив Java в Iterable


150

У мене є масив примітивів, наприклад для int, int [] foo. Це може бути невеликий розмір, чи ні.

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

Який найкращий спосіб створити Iterable<Integer>з нього?

Iterable<Integer> fooBar = convert(foo);

Примітки:

Будь ласка, не відповідайте за допомогою циклів (якщо ви не можете дати чітке пояснення щодо того, як компілятор робить щось розумне з ними?)

Також зауважте, що

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

Навіть не складатимуть

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

Також перевірте, чому масив не призначається Iterable? перш ніж відповісти.

Крім того, якщо ви використовуєте якусь бібліотеку (наприклад, Guava), поясніть, чому це найкраще. (Тому що його від Google не є повною відповіддю: P)

Нарешті, оскільки, здається, є домашнє завдання з цього приводу, уникайте публікації домашнього коду.


можливий дублікат Iterator для масиву
NPE

Додайте їх до LinkedList, а потім просто поверніть ітератор цього набору.

Відповіді:


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);

Хоча для цього вам потрібно використовувати Integerмасив (а не intмасив).

Для примітивів можна використовувати гуаву:

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

Для Java8: (з відповіді Джина Кваона)

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
Дві ноти: 1) він має int, а не Integer2) List, Iterableтому третій рядок є безглуздим.
Максимов

1
йому потрібен Ітерабел, чому існує третій рядок.
fmucar

5
2-й і 3-й рядки - це варіанти, які я б сказав :)
fmucar

1
Це не є частиною домашнього завдання, я просто намагався уникати дублювання коду для функції налагодження, що обробляє вміст масиву чи списку ... Оглядаючи навколо, я дійсно знайшов Arrays.asList (..) ;, але принаймні Eclipse, здається, вважає, що це не буде робити те, що я хочу (наприклад, це підводить результат Arrays.asList (foo) як List <int []>, а не List <Integer> ...) Я вважав це досить цікавим для питання ... (руйнує коментар в частині причини limits-)
NTG

1
Взагалі можна було б придумати безліч способів зробити це, але мені було цікаво, що найкраще (наприклад, петля буде набагато повільніше порівняно з ... {ну, проблема в тому, що я нічого не можу придумати !: )}) Також перевірте stackoverflow.com/questions/1160081/…, щоб обговорити, чому, моє питання хоч і не чому, а як і який тип контейнера був би найкращим (чому ArrayList? Насправді, я міг уявити собі якийсь AbstractList обгортка з використанням Generics .., ймовірно, залежить від розміру ...)
ntg

44

лише мої 2 копійки:

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
delete () не потрібен в java 8, тому що це метод за замовчуванням, який видає UnsupportedOperationException. Тільки якщо ви хочете надіслати краще пояснення.
Олексій

+1 Я щось подібне роблю, щоб створити Iterator<Character>з String. Реалізація власного Iteratorвиглядає як єдиний спосіб уникнути непотрібного повторення всіх значень для перетворення з об’єктного типу в примітивний тип ( Ints.asList()наприклад, наприклад, через Guava ), просто щоб отримати можливість отримати Iteratorте, Listщо було створено.
spaaarky21

2
Ви праві, Алекс. Методи за замовчуванням були додані до Java 8. У 2013 році я додав сюди цей старовинний фрагмент коду.
Йорг Рутшиллінг

29

З Java 8 ви можете це зробити.

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 надає адаптер, який ви хочете отримати як Int.asList () . Існує еквівалент для кожного примітивного типу в асоційованому класі, наприклад, Booleansдля booleanтощо.

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);
}

Пропоновані вище пропозиції Arrays.asListне будуть працювати, навіть якщо вони складені, тому що ви отримаєте Iterator<int[]>скоріше Iterator<Integer>. Що відбувається, що замість того, щоб створити список, підкріплений масивом, ви створили 1-елементний список масивів, що містить ваш масив.


лише зауваження: посилання більше не працює. Посилання на Github: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle

Дякую @Passi, виправлено (не можу більше знайти спосіб, який підтримує Google, щоб зв’язатися з javadoc, тому я зв’язав джерело, яке ви надали).
BeeOnRope

8

У мене була така ж проблема і вирішувалася так:

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

Сам ітератор - лінивий, UnmodifiableIteratorале саме це мені було потрібно.


7

У Java 8 або новіших версіях Iterableфункціональний інтерфейс повертається Iterator. Так ви можете це зробити.

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

->

1
2
3

3

Перш за все, я можу погодитись лише з тим, що Arrays.asList(T...), очевидно, найкраще рішення для типів Wrapper або масивів із непомітними типами даних. Цей метод викликає конструктор простої приватної статичної AbstractListреалізації в Arraysкласі, який в основному зберігає дану посилання масиву як поле і моделює список, замінюючи необхідні методи.

Якщо ви можете обрати між примітивним типом або типом Wrapper для свого масиву, я б використовував тип Wrapper для таких ситуацій, але, звичайно, це не завжди корисно або потрібно. Ви можете зробити лише дві можливості:

1) Ви можете створити клас зі статичним методом для кожного примітивного масиву даних ( boolean, byte, short, int, long, char, float, doubleповернення Iterable<WrapperType >. Ці методи використовуватимуть анонімні класи Iterator(до того жIterable), яким дозволяється містити посилання на аргумент містить метод (наприклад, an int[]) як поле для реалізації методів.

-> Цей підхід є ефективним і економить вашу пам'ять (за винятком пам'яті новостворених методів, навіть якщо використання Arrays.asList()забирає пам'ять так само)

2) Оскільки в масивах немає методів (як слід читати на стороні) ви пов’язані) вони також не можуть надати Iteratorпримірник. Якщо ви справді лінуєтеся писати нові класи, ви повинні використовувати екземпляр вже існуючого класу, який реалізує, Iterableоскільки іншого способу є, крім інстанції Iterableабо підтипу.
ТІЛЬКИ спосіб створення наявної реалізації похідної колекціїIterableце використовувати цикл (за винятком використання анонімних класів, як описано вище) або інстанціювати Iterableклас реалізації, конструктор якого допускає масив примітивних типів (бо Object[]не дозволяє масиви з елементами примітивного типу), але наскільки я знаю, Java API не містить такого класу.

Причину циклу можна легко пояснити:
для кожної колекції вам потрібні об'єкти, а примітивні типи даних не є об'єктами. Об'єктів набагато більше, ніж примітивні типи, тому вони потребують додаткових даних, які необхідно генерувати для кожного елемента масиву примітивного типу. Це означає, що якщо два способи з трьох (використання Arrays.asList(T...)або використання наявної колекції) вимагають сукупності об'єктів, вам потрібно створити для кожного примітивного значення вашогоint[]масив об’єкта обгортки. Третій спосіб буде використовувати масив таким, який є, і використовувати його в анонімному класі, оскільки я вважаю, що це краще через швидку продуктивність.

Існує також третя стратегія, що використовує Objectяк аргумент для методу, де ви хочете використовувати масив, Iterableі для цього потрібно буде перевірити тип, щоб визначити, який тип має аргумент, однак я б не рекомендував його взагалі, як зазвичай потрібно врахуйте, що Об'єкт не завжди потрібний тип і вам потрібен окремий код для певних випадків.

На закінчення, це проблема проблемної системи Generic Type Java, яка не дозволяє використовувати примітивні типи як загальний тип, що дозволить заощадити багато коду, використовуючи простоArrays.asList(T...). Отже, вам потрібно запрограмувати для кожного масиву примітивних типів такий собі метод (який в основному не має ніякої різниці в пам'яті, що використовується програмою C ++, який створив би для кожного використовуваного аргументу типу окремий метод.


3

Ви можете використовувати IterableOfвід кактусів :

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

Потім ви можете перетворити його у список за допомогою ListOf:

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

Або просто так:

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

1

Хоча подібну відповідь уже було розміщено, я вважаю, що причина використання нового PrimitiveIterator.OfInt була не ясна. Хорошим рішенням є використання Java 8 PrimitiveIterator, оскільки він спеціалізований для примітивних типів int (і дозволяє уникнути додаткового штрафу за бокс / unboxing):

    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
    }

Посилання: https://doc.bccnsoft.com/docs/jdk8u12-docs/api/java/util/PrimitiveIterator.OfInt.html


-2

У java8 потік IntSteam може бути розміщений у вікні потоку Integers.

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

Я думаю, що ефективність має значення залежно від розміру масиву.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.