繁体   English   中英

是否有任何Java标准类在没有实现Collection的情况下实现Iterable?

[英]Are there any Java standard classes that implement Iterable without implementing Collection?

我有一个难题,让我思考是否有任何标准的java类实现Iterable<T>而不实现Collection<T> 我正在实现一个接口,要求我定义一个接受Iterable<T> ,但是我用来支持这个方法的对象需要Collection<T>

这让我做了一些真正的kludgy感觉代码,在编译时会给出一些未经检查的警告。

public ImmutableMap<Integer, Optional<Site>> loadAll(
        Iterable<? extends Integer> keys
) throws Exception {
    Collection<Integer> _keys;
    if (keys instanceof Collection) {
        _keys = (Collection<Integer>) keys;
    } else {
        _keys = Lists.newArrayList(keys);
    }

    final List<Site> sitesById = siteDBDao.getSitesById(_keys);
    // snip: convert the list to a map

更改我生成的集合以使用更通用的Collection<? extends Integer> Collection<? extends Integer> type不会消除该行的未经检查的警告。 此外,我无法更改方法签名以接受Collection而不是Iterable因为它不再覆盖super方法,并且在需要时不会被调用。

似乎没有解决这个演员或复制问题的方法:其他地方已经提出了其他问题,而且它似乎深深植根于Java的通用和类型擦除系统。 但我要问的是,是否有任何类可以实现Iterable<T> ,而且还没有实现Collection<T> 我已经浏览了Iterable JavaDoc ,当然我希望传递给我的界面的所有内容实际上都是一个集合。 我想使用一个在野外预编写的类,因为它似乎更有可能实际作为参数传递,并使单元测试更有价值。

由于我正在写的一些单元测试,我确定我编写的演员或复制位与我在项目中使用它的类型一起工作。 但我想编写一个单元测试的一些输入一个可迭代但不是一个集合,到目前为止,所有我已经能够拿出正在实施一个虚拟的测试类实现自己。


好奇的是,我正在实现的方法是Guava的CacheLoader<K, V>.loadAll(Iterable<? extends K> keys) ,而后台方法是一个JDBI实例化的数据访问对象,它需要一个集合用作@BindIn接口的参数类型。 我认为我认为这是问题的正确性是正确的,但以防万一有人想尝试横向思考我的问题。 我知道我可以只分叉JDBI项目并重写@BindIn注释以接受可迭代的...

虽然没有一个课程可以立即满足您的需求,并且对测试代码的读者很直观,但您可以轻松创建自己的匿名类,这个类很容易理解:

static Iterable<Integer> range(final int from, final int to) {
    return new Iterable<Integer>() {
        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                int current = from;
                public boolean hasNext() { return current < to; }
                public Integer next() {
                    if (!hasNext()) { throw new NoSuchElementException(); }
                    return current++;
                }
                public void remove() { /*Optional; not implemented.*/ }
            };
        }
    };
}

演示。

此实现是匿名的,它不实现Collection<Integer> 另一方面,它产生一个非空的可枚举整数序列,您可以完全控制它。

按标题回答问题:

是否有任何Java标准类在没有实现Collection情况下实现Iterable

来自文字:

如果有任何类可以实现Iterable<T>而不实现Collection<T>

回答:

请参阅以下javadoc页面: https//docs.oracle.com/javase/8/docs/api/java/lang/class-use/Iterable.html

任何说明Classes in XXX that implement Iterable部分都将列出实现该接口的Java标准类。 其中许多都没有实现Collection

Kludgy,是的,但我认为代码

Collection<Integer> _keys;
if (keys instanceof Collection) {
    _keys = (Collection<Integer>) keys;
} else {
    _keys = Lists.newArrayList(keys);
}

听起来很完美。 接口Collection<T>扩展了Iterable<T>并且不允许您使用2个不同的类型参数实现相同的接口,因此例如,类无法实现Collection<String>Iterable<Integer>

Integer类是final,所以Iterable<? extends Integer> Iterable<? extends Integer>Iterable<Integer>主要是学术性的。

总而言之,最后两段证明,如果某些东西都是Iterable<? extends Integer> Iterable<? extends Integer>Collection ,它必须是Collection<Integer> 因此,您的代码保证是安全的。 编译器无法确定这一点,因此您可以通过编写来抑制警告

@SuppressWarnings("unchecked")

在声明之上。 您还应该在注释中包含注释,以解释代码安全的原因。

至于是否有任何类实现Iterable而不是Collection ,正如其他人指出的那样答案是肯定的。 但是我认为你真正要问的是有两个接口是否有任何意义。 许多人都问过这个。 通常当一个方法有一个Collection参数时(例如addAll()它可能,也可能应该是一个Iterable

编辑

@Andreas在评论中指出, Iterable仅在Java 5中引入,而Collection是在Java 1.2中引入的,并且大多数现有的采用CollectionIterable由于兼容性原因而无法进行改进以采用Iterable

在核心API中,唯一的类型是Iterable而不是Collection -

interface java.nio.file.Path

interface java.nio.file.DirectoryStream
interface java.nio.file.SecureDirectoryStream

class java.util.ServiceLoader

class java.sql.SQLException (and subclasses)

可以说这些都是糟糕的设计。

正如@ bayou.io的回答中提到的, Iterable一个这样的实现是Java 7中引入的文件系统遍历的新Path类。

如果您碰巧使用Java 8, Iterable已经改装(即给定一个default方法) spliterator() (注意其实现说明 ),这使您可以将它与StreamSupport结合使用:

public static <T> Collection<T> convert(Iterable<T> iterable) {
    // using Collectors.toList() for illustration, 
    // there are other collectors available
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}

这是因为任何已经是Collection实现的参数都经历了不必要的流和收集操作。 您可能只应该使用它,如果与原始的转换或基于Guava的方法相比,对标准化JDK方法的需求超过潜在的性能损失,这可能没有用,因为您已经使用了Guava的CacheLoader

要测试这个,请考虑以下代码段和示例输出:

// Snippet
System.out.println(convert(Paths.get(System.getProperty("java.io.tmpdir"))));
// Sample output on Windows
[Users, MyUserName, AppData, Local, Temp]

在阅读了优秀的答案并提供了文档之后,我在几个课程中进行了讨论,并找到了看起来像是胜利者的东西,无论是直接测试代码还是直接问题。 Java的主要ArrayList实现包含这个gem:

public Iterator<E> iterator() {
    return new Itr();
}

其中Itr是一个私有内部类,具有Iterator<E>的高度优化的自定义实现。 不幸的是, Iterator本身并没有实现Iterable ,所以如果我想把它变成我的帮助器方法来测试不执行转换的代码路径,我必须将它包装在我自己的实现Iterable的垃圾类中(和不Collection )并返回Itr 这是一种方便的方法,可以轻松地将集合转换为Iterable而无需自己编写迭代代码。

最后一点,我的代码的最终版本甚至没有进行转换本身,因为Guava的Lists.newArrayList几乎完全与我在问题中使用运行时类型检测做了什么。

@GwtCompatible(serializable = true)
public static <E> ArrayList<E> More ...newArrayList(Iterable<? extends E> elements) {
  checkNotNull(elements); // for GWT
  // Let ArrayList's sizing logic work, if possible
  if (elements instanceof Collection) {
    @SuppressWarnings("unchecked")
    Collection<? extends E> collection = (Collection<? extends E>) elements;
    return new ArrayList<E>(collection);
  } else {
    return newArrayList(elements.iterator());
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM