7

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

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

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

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

4

6 回答 6

9

我建议使用Iterable,CollectionList作为参数类型。

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制作收集数据的(浅)副本。如果您要返回一个数组,那么制作副本通常是您想要的。不对称地,如果您作为参数传递,则通常不会复制(除非存储为字段)。

于 2009-04-20T18:33:05.953 回答
5

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

正如你所说,Iterable不会处理数组。

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

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

我还是会和Iterable这里一起去。我更喜欢它,Collection因为过去我有实现Iterable但不是集合的类;这使他们可以轻松地根据需要懒惰地检索值,并且我可以对它们使用 foreach 循环。

于 2009-04-20T18:26:56.420 回答
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)中出现两个编译时错误;所以你必须设计你的方法来接受一个迭代(集合)和/或一个数组,至少一个方法必须执行一些转换(到/从数组)

解决方法

您可以尝试使用适配器:

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 + 匿名类很酷

希望能帮助到你

于 2009-04-20T18:57:11.090 回答
1

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

冗长的答案:由于增强的for-loop 被有效地定义了两次(一次用于数组,一次用于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 的概述将数组包装在您自己的中)。

于 2009-04-20T19:35:17.007 回答
0

Java 1.5+ 中的 Java 泛型有一个鲜为人知的特性,您可以<? 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 中使用该基类的任何方法(如果您的参数为 ,则可以使用 Object 类的任何方法<? extends Object>)。有一个类似的特性<? super MyType>,它接受一个参数,该参数是给定类型的超类型,而不是子类型。以这种方式可以使用“extends”和“super”有一些限制。这是查找更多信息的好地方

于 2009-04-20T19:28:50.220 回答
-3
public static void myFun(Collection<MyClass> collection) {
    for (MyClass mc : collection) {
        // Stuff is processed here
    }
}
于 2009-04-20T18:27:55.950 回答