我可以在Java中使用的参数适用于所有for-each循环吗?

时间:2009-04-20 18:25:49

标签: java iterator foreach

假设我有一个方法接受一个数组并使用内置for-each loop内置的Java处理其中的每个元素,如下所示:

public static void myFun(SomeClass[] arr) {
    for (SomeClass sc : arr) {
        // Stuff is processed here
    }
}

这很好用,但现在我希望能够传递相同的方法List<SomeClass>。我是否注定要使用Collection.toArray(T []),还是我可以使用myFun()接受任何可以在for-each构造中使用的类型的参数?

澄清一下:我想要一个接受任何可迭代对象的方法签名,无论是原始数组还是Collection。我可以很容易地编写两种方法,一种方法包装另一种方法,但我只是好奇是否有更好的方法。

6 个答案:

答案 0 :(得分:9)

我建议使用IterableCollectionList作为参数类型。

IMO,集合应该优先于引用数组。如果您碰巧有一个数组Arrays.asList,那么转换很顺利。 Arrays.asList允许获取并返回到数组,但显然不是“结构”修改会改变数组长度。

myFun(Arrays.asList(arr));

您可能必须在极端/一般情况下使用通配符。

public static void myFun(Iterable<? extends SomeClass> somethings) {
    for (SomeClass something : somethings) {
        // something is processed here
    }
}

值得注意的是Collections.toArrayArrays.asList的工作方式略有不同。 asList使原始数组保留回集合,因此对集合的更改将反映在数组中。 Collections.toArray生成集合数据的(浅)副本。如果要返回数组,那么复制通常是您想要的。不对称,如果您作为参数传递,通常不会复制(除非存储为字段)。

答案 1 :(得分:5)

使用Iterable。这就是它的用途。

正如你所说,Iterable将无法处理数组。

您不希望使用多个方法相互包装。这排除了Arrays.asListCollection.toArray

所以你的问题的答案是否定的,没有办法。但是,如果您可以使用Lists,为什么要使用数组?

我仍然会在这里Iterable。我比Collection更喜欢它,因为在过去我有过实现Iterable但不是集合的类;这使得他们可以根据需要轻松地检索值,并且可以在它们上使用foreach循环。

答案 2 :(得分:3)

你不能,java Arrays不实现Iterable:

public static int sum(Iterable<Integer> elements) {
    int s = 0;

    for (int i : elements) {
        s += i;
    }

    return s;
}

public static void main(String[] args) {
    L1: System.out.println(sum(1,2,3));
    L2: System.out.println(sum(Arrays.asList(1,2,3)));
    L3: System.out.println(sum(new int[] { 1,2,3 }));
}

这导致(L1和L3)中的两个编译时错误;所以你必须设计你的 接受Iterable(Collections)和/或Array的方法,至少有一个方法必须执行一些转换(到/从数组)

解决方法

您可以尝试使用适配器:

public class ArrayIterator<T> implements Iterator<T> {

    private final T[] array;
    private int i;

    public ArrayIterator(T[] anArray) {
        array = anArray;
        i = 0;
    }

    public boolean hasNext() {
        return i < array.length;
    }

    public T next() {
        return array[i++];
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

private static int sum(final Integer ... elements) {
    return sum(new Iterable<Integer>() {

        public Iterator<Integer> iterator() {
            return new ArrayIterator<Integer>(elements);
        }
    });
}

只有在处理原始数组时才应注意;当你只使用 引用对象(您的情况)ArrayIterator +匿名类很酷

希望有所帮助

答案 3 :(得分:1)

简短回答:不,没有单一的方法签名类型 - 安全地接受Iterable和数组。显然你可以接受Object,但这也是一个黑客攻击。

Long-ish回答:由于增强的for - 循环被有效地定义了两次(一次用于数组,一次用于Iterable),你还需要提供两个重载方法:

public static void myFun(SomeClass[] array) {
    for (SomeClass sc : array) {
        doTheProcessing(sc);
    }
}

public static void myFun(Iterable<? extends SomeClass> iterable) {
    for (SomeClass sc : iterable) {
        doTheProcessing(sc);
    }
}

尽管两种方法的来源看起来完全相同,但您需要定义两次(除非您将数组包装在您自己的Iterable中,如@dfa所述)。

答案 4 :(得分:0)

Java 1.5+中有一些Java Generics的特性,您可以在方法调用和构造函数中使用<? extends Subtype>。您可以使用<? extends Object>,然后处理这些内容的任何内容都只能访问Object上的方法。你可能真正想要的是更像这样的东西:

List<? extends MyCrustaceans> seaLife = new ArrayList<? extends MyCrustaceans>();
MyShrimp s = new MyShrimp("bubba");
seaLife.add(s);
DoStuff(seaLife);

...

public static void DoStuff(List<? extends MyCrustaceans> seaLife)
{
    for (MyCrustaceans c : seaLife) {
        System.out.println(c);
    }
}

因此,如果您有一个基类(如MyCrustaceans),您可以在DoStuff中使用该基类的任何方法(如果参数为<? extends Object>,则可以使用Object类中的任何方法)。 <? super MyType>有一个类似的特性,它接受一个给定类型的超类型的参数,而不是子类型。对于你可以用这种方式使用“扩展”和“超级”的东西有一些限制。这是一个good place to find more info

答案 5 :(得分:-3)

public static void myFun(Collection<MyClass> collection) {
    for (MyClass mc : collection) {
        // Stuff is processed here
    }
}