我有一个基元数组,例如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>
在回答之前还要检查Why is an array not assignable to Iterable?。
此外,如果你使用一些库(例如,番石榴),请解释为什么这是最好的。 (因为它来自谷歌不是一个完整的答案:P)
最后,由于似乎有关于此的功课,避免发布家庭作业代码。
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 :(来自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();
在java8中,IntSteam流可以装箱为整数流。
public static Iterable<Integer> toIterable(int[] ints) {
return IntStream.of(ints).boxed().collect(Collectors.toList());
}
我认为性能很重要,这取决于阵列的大小。
只需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.");
}
};
}
};
使用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();
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元素数组列表,而不是创建由数组支持的列表。
我遇到了同样的问题并解决了这个问题:
final YourType[] yourArray = ...;
return new Iterable<YourType>() {
public Iterator<YourType> iterator() {
return Iterators.forArray(yourArray); // Iterators is a Google guava utility
}
}
迭代器本身是一个懒惰的UnmodifiableIterator
,但这正是我需要的。
首先,我只能同意Arrays.asList(T...)
显然是Wrapper类型或具有非原始数据类型的数组的最佳解决方案。此方法在AbstractList
类中调用一个简单的私有静态Arrays
实现的构造函数,它基本上将给定的数组引用保存为字段,并通过覆盖所需的方法来模拟列表。
如果您可以为数组选择原始类型或Wrapper类型,我会在这种情况下使用Wrapper类型,但当然,它并不总是有用或必需。你可以做的只有两种可能性:
1)你可以为每个原始数据类型数组创建一个带静态方法的类(boolean, byte, short, int, long, char, float, double
返回一个Iterable<
WrapperType>
。这些方法将使用Iterator
的匿名类(除了Iterable
),允许包含contains方法参数的引用(例如int[]
)作为字段以实现方法。
- >这种方法性能很好并且可以节省你的内存(除了新创建的方法的内存,即使使用Arrays.asList()
会以相同的方式记忆)
2)由于数组没有方法(在你链接的一边读取),它们也不能提供Iterator
实例。如果你真的懒得编写新类,你必须使用一个实现Iterable
的已经存在的类的实例,因为除了实例化Iterable
或子类型之外别无他法。
创建实现Iterable
的现有Collection衍生的唯一方法是使用循环(除了你使用如上所述的匿名类)或者实例化一个Iterable
实现类,其构造函数允许一个原始类型数组(因为Object[]
不允许带有原语的数组类型元素)但据我所知,Java API没有这样的类。
循环的原因可以很容易地解释:
对于您需要的每个集合对象和原始数据类型不是对象。对象比原始类型大得多,因此它们需要为基本类型数组的每个元素生成必须生成的附加数据。这意味着如果两种方式(使用Arrays.asList(T...)
或使用现有Collection)需要聚合对象,则需要为int[]
数组的每个原始值创建包装器对象。第三种方式将按原样使用数组并在匿名类中使用它,因为我认为由于快速性能,它更可取。
还有第三种策略使用Object
作为你要使用数组或Iterable
的方法的参数,它需要类型检查来确定参数的类型,但是我不会像你通常那样推荐它需要考虑Object并不总是所需的类型,并且您需要为某些情况分开代码。
总而言之,这是Java有问题的通用类型系统的错误,它不允许使用原始类型作为通用类型,通过使用简单的Arrays.asList(T...)
可以节省大量代码。所以你需要为每个基本类型数组编程,你需要这样一个方法(这对于C ++程序使用的内存基本上没有区别,C ++程序将为每个使用的类型参数创建一个单独的方法。
你可以使用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"
);
在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
虽然类似的答案已经发布了,但我认为使用新的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