【问题标题】:Is there a parameter I can use in Java that works with all for-each loops?我可以在 Java 中使用适用于所有 for-each 循环的参数吗?
【发布时间】:2010-10-20 15:35:09
【问题描述】:

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

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

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

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

【问题讨论】:

  • 很遗憾,答案是否定的,因为数组没有实现 Iterable。

标签: java iterator foreach


【解决方案1】:

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

【讨论】:

  • 我同意;对于这样的情况,我使用 Iterable,因为它给实现者带来的负担最小。虽然它不能直接与 Object[] 一起使用,但将其与 Arrays.asList() 连接起来很简单。
  • 是的。 OTOH,实现可能需要在迭代之前收集大小,并且大多数代码仍然使用 Collection。
  • 鉴于似乎没有办法避免 asList() 或 toArray(),我最同意这个逻辑;如果我期望两者都有输入,那么使用 Iterable 而不是 Object[] 是有意义的。
  • (添加了一段关于 asList vs toArray)
【解决方案2】:

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

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

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

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

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

【讨论】:

  • 如果你使用 Iterable 作为参数,那么当你尝试传入原始数组时会出现编译错误。
  • 好吧,我想这是与 OO 相关的。你不应该在数组周围写一个包装器吗?
【解决方案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 + 匿名类很酷

希望对你有帮助

【讨论】:

    【解决方案4】:

    简短的回答:不,没有一个方法签名可以安全地接受 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);
        }
    }
    

    虽然这两种方法的来源看起来完全一样,但您需要定义两次(当然,除非您按照@dfa 的说明将数组包装在自己的Iterable 中)。

    【讨论】:

    • "澄清一下:我想要一个可以接受任何可迭代对象的方法签名,无论是原始数组还是集合。我可以很容易地编写两个方法,一个包装另一个,但我我只是好奇是否有更好的方法。”
    • 好的,那我没看清楚这个问题。我希望这个更好。
    • 是的,真的没有其他答案了。所有的“答案”都只是解决方法或建议。
    【解决方案5】:

    在 Java 1.5+ 中有一个鲜为人知的 Java 泛型特性,您可以在方法调用和构造函数中使用 &lt;? extends Subtype&gt;。你可以使用&lt;? extends Object&gt;,然后任何处理这些的东西都只能访问对象上的方法。你可能真正想要的是更像这样的东西:

    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 类的任何方法,如果您的参数是 &lt;? extends Object&gt;)。 &lt;? super MyType&gt; 有一个类似的特性,它接受一个参数,该参数是给定类型的超类型,而不是子类型。以这种方式可以使用“extends”和“super”有一些限制。这是good place to find more info

    【讨论】:

      【解决方案6】:
      public static void myFun(Collection<MyClass> collection) {
          for (MyClass mc : collection) {
              // Stuff is processed here
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-09
        • 2021-11-08
        • 2021-06-04
        • 2013-09-01
        • 1970-01-01
        相关资源
        最近更新 更多