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

时间:2015-09-14 17:31:22

标签: java generics collections iterable

我有一个难题,让我思考是否有任何标准的java类在没有实现Iterable<T>的情况下实现Collection<T>。我实现了一个界面,要求我定义一个接受Iterable<T>的方法,但我用来支持此方法的对象需要Collection<T>

这让我做了一些非常狡猾的感觉代码,在编译时会给出一些未经检查的警告。

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而不是Iterable,因为它不再覆盖超级方法,并且不会在调用时被调用需要的。

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

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

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

6 个答案:

答案 0 :(得分:11)

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

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.*/ }
            };
        }
    };
}

Demo.

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

答案 1 :(得分:10)

按标题回答问题:

  

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

来自文字:

  

如果有任何类可以实现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

答案 2 :(得分:10)

Kludgy,是的,但我认为代码

Collection<T>

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

课程Iterable<? extends Integer>是最终的,因此Iterable<Integer>Iterable<? extends Integer>之间的差异主要是学术性的。

总而言之,最后两段证明,如果某个内容同时属于CollectionCollection<Integer>,则必须为@SuppressWarnings("unchecked") 。因此,您的代码保证是安全的。编译器无法确定这一点,因此您可以通过编写

来抑制警告
Iterable

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

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

修改

@Andreas在评论中指出Collection仅在Java 5中引入,而Collection是在Java 1.2中引入的,而大多数现有方法采用Iterable都不能因兼容性原因而改装为library(ggplot2) library(dplyr) set.seed(272727) data <- data.frame(year = sample(2001:2011, 100, replace = TRUE), string = sample(letters[1:5], 100, replace = TRUE)) # this is what will be plotted table(data$string, data$year) dataSummary <- as.data.frame(xtabs(~year+string, data)) ggplot(dataSummary, aes(x = year, y = Freq, group = string, colour = string)) + geom_line()

答案 3 :(得分:5)

在核心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)

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

答案 4 :(得分:1)

正如@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]

答案 5 :(得分:0)

在阅读了优秀的答案并提供了文档之后,我在几个课程中进行了探讨,并且在测试代码的直接性和直接问题标题方面找到了看起来像是赢家的东西。 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());
  }
}