Конвертировать массив 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)

И последнее, так как кажется, что это домашнее задание, не размещайте домашний код.

NTG
источник
возможный дубликат Iterator для массива
NPE
Добавьте их в LinkedList, а затем просто верните итератор этого набора.

Ответы:

118
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массив), чтобы это работало.

Для примитивов вы можете использовать guava:

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();
fmucar
источник
10
Две заметки: 1) у него есть int, а не Integer2) Listуже Iterableтак третья строка бессмысленна.
Максимов
1
ему нужен Iterable, поэтому есть третья строка.
fmucar
5
2-я и 3-я строки - варианты, я бы сказал :)
fmucar
1
Это не часть домашней работы, я просто пытался избежать дублирования кода для функции отладки, обрабатывающей содержимое массива или списка ... Оглядываясь, я действительно нашел Arrays.asList (..) ;, но по крайней мере Eclipse, кажется, думает, что он не будет делать то, что я хочу (например, он выводит результат Arrays.asList (foo) в виде List <int []>, а не List <Integer> ...) Я нашел это достаточно интересным для вопрос ... (комментирует частичные причины ограничений)
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
remove () не является обязательным в Java 8, потому что это метод по умолчанию, который выбрасывает UnsupportedOperationException. Только если вы хотите предоставить лучшее объяснение сообщения.
Алекс
+1 Я делаю нечто подобное, чтобы создать Iterator<Character>из String. Реализация собственной Iteratorкажется единственным способом избежать ненужной итерации всех значений для преобразования из типа объекта в примитивный тип (например, через Guava Ints.asList()), просто чтобы получить возможность получить 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-элементный список массивов, содержащий ваш массив.

BeeOnRope
источник
просто примечание: ссылка больше не работает. Ссылка на 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
saka1029
источник
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аргумент as для метода, в котором вы хотите использовать массив, или Iterableтребующая проверки типов, чтобы выяснить, какой тип имеет аргумент, однако я бы не рекомендовал его вообще, поскольку вам обычно требуется Учтите, что объект не всегда имеет требуемый тип, и вам нужен отдельный код для определенных случаев.

В заключение, это вина проблематичной системы Generic Type, которая не позволяет использовать примитивные типы в качестве универсального типа, что позволило бы сэкономить много кода, просто используяArrays.asList(T...), Таким образом, вам нужно запрограммировать для каждого массива примитивного типа, вам нужен такой метод (который в принципе не имеет значения для памяти, используемой программой C ++, которая создаст для каждого используемого аргумента типа отдельный метод).

CodingVirus01
источник
3

Вы можете использовать IterableOfот Cactoos :

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"
);
yegor256
источник
1

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

    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 может быть упакован в поток целых чисел.

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

Я думаю, что производительность имеет значение в зависимости от размера массива.

Сумьяканта Мохапатра
источник