通用枚举到可迭代转换器 [关闭]

Posted

技术标签:

【中文标题】通用枚举到可迭代转换器 [关闭]【英文标题】:generic Enumeration to Iterable converter [closed] 【发布时间】:2012-01-02 02:59:33 【问题描述】:

HttpServletRequest 使用了大量的 java.util.Enumeration。我想在 for-each 中使用它们,所以我需要将它们转换为可交互的。这不是问题,但是因为我有多个项目需要这个,所以我需要一个库来做到这一点。我宁愿不自己做——有没有支持这种装饰的标准库?

是否有内置构造可以将 Enumeration 转换为 Iterable?

【问题讨论】:

拥有 Iterator->Iterable 也很好,而且没有转换为列表的 O(n) 成本。 为 google guava 添加了增强票:code.google.com/p/guava-libraries/issues/detail?id=796 @Micheal, IAdapter - 从我引用的 javadoc 可以看出,他们无意这样做。票证也已提到idea graveyard,我会在我的回答中给出解释的要点 有趣的是:HttpServletResponse 使用了更常见的 Collection 接口。 我不同意这是题外话 - 提问者要求的解决方案可能是也可能不是图书馆 【参考方案1】:

看看这篇文章:http://www.java2s.com/Code/Java/Collections-Data-Structure/TreatanEnumerationasanIterable.htm

这似乎正是您所需要的。

更新 添加代码以供将来参考,以防链接损坏。

import java.util.Enumeration;
import java.util.Iterator;

/**
 * @since 4.37
 * @author Jaroslav Tulach
 */
public class Utils 
  public static <E> Iterable<E> iterable(final Enumeration<E> enumeration) 
      if (enumeration == null) 
          throw new NullPointerException();
      
      return new Iterable<E>() 
          public Iterator<E> iterator() 
              return new Iterator<E>() 
                  public boolean hasNext() 
                      return enumeration.hasMoreElements();
                  
                  public E next() 
                      return enumeration.nextElement();
                  
                  public void remove() 
                      throw new UnsupportedOperationException();
                  
              ;
          
      ;
      

【讨论】:

不,这根本不是我需要的。正如我所说,编写这段代码很容易,这不是我想要的。 @IAdaptor 你能提供更多信息吗?为什么这不是你想要的? 该代码的问题在于,通过 Iterable 创建的所有迭代器都将使用相同的枚举器。 @raisercostin,恕我直言,这根本不是问题。 Iterable 是定义方法Iterator 的接口,该方法可以在每次调用时返回不同的迭代器。如果你用实现接口 Enumeration 的东西包装这个迭代器,你将使用从 iterator() 返回的迭代器。 不幸的是,如果您从与此处 ideone.com/4BxbiM 相同的枚举器创建两个新迭代器,则第一个迭代将消耗基本枚举器,并且不会为第二个迭代器留下任何内容。【参考方案2】:

java.util.Collections 有一个list 方法,可以将Enumeration 复制到List,然后您可以在for-each 循环中使用它(请参阅javadoc)。

【讨论】:

您不能在 for-each 循环中直接使用 Iterator @Jeffrey:该死,你是对的,我没有考虑清楚(或正确阅读问题)。固定。 @Jeffrey 除非你把它包裹在 new Iterable&lt;E&gt;()public Iterator&lt;E&gt; iterator()return Iterators.forEnumeration(enum);【参考方案3】:

这里是来自 Guava 的javadoc,关于将枚举转换为迭代器(不可迭代):

公共静态 UnmodifiableIterator forEnumeration(Enumeration枚举)

使枚举适应迭代器接口。

此方法在 Iterables 中没有等效方法,因为查看 枚举作为 Iterable 是不可能的。不过内容可以 使用复制到集合中 Collections.list(java.util.Enumeration)。

更多 apache commons collections 当前实现不支持 Java 5 功能和 API,例如 Iterable,因此没有机会。

然而,这些库中有一些方法允许您将枚举更改为可迭代的集合并使用它(尽管它们会隐式复制您的数据)。

例如,转换为带有EnumerationUtils.toList(enumeration) 的列表。

编辑:由于问题中的一些疑问,我将尝试总结为什么番石榴的制造者(和我)不觉得可以将枚举变成可迭代的。

一个可迭代对象创建迭代器实例,阅读代码(或 API)的人可以假设每次调用 iterator() 都会产生一个新的迭代器实例,从第一个元素开始枚举。如果我们在迭代器(或枚举)和可迭代对象之间进行简单的转换,那么 API 的用户需要知道对 iterator() 的调用会改变对象的状态,并且两次连续调用可能会表现得很奇怪。这是一个例子:

Iterable<String> iter = magicFunction(enumeration);
for (String s : iter) 
  if ("hello".equals(s))
    break;


for (String s : iter) 
  if ("world".equals(s))
    return true;

return false;

如果实现为简单转换 ( O(1) ) 对于不同的输入,上述方法的行为会有所不同:["hello","world"] 将返回 true,而 ["world","hello"] 将返回 false。这在查看代码时不会立即显现出来,并且可能是许多令人沮丧的错误的原因。因此,我认为不存在此实用方法是有意义的。

【讨论】:

关于 Guava 的“不可能”声明的愚蠢之处在于,我们实际上已经在我们的实用程序方法中实现了这个“不可能”Iterable。由于我们调用方法 toSingleUseIterable(),调用者很明显它只会工作一次。 魔术函数返回的Iterable可以缓存输出,只有在迭代通过缓存的索引时才继续使用enumeration.next()。 确实如此,这将节省每次调用该方法的 O(n) 成本,并用 O(n) 内存开销替换它。主要的缺点是您需要在迭代器的实例之间共享缓存,这可能会导致同步开销或潜在的错误,或者您可以将缓存复制到您构建的迭代器的任何实例中,但您可能只是复制整个枚举并用它完成 在 Java 8 中你可以使用:Iterators.forEnumeration(oldEnumeratorMethod()).forEachRemaining(e -&gt; …);【参考方案4】:

在我看来,文档化的方式是最简单的,所以不需要转换成迭代器:

https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Enumeration.html

for (Enumeration<E> e = v.elements(); e.hasMoreElements();)
   System.out.println(e.nextElement());

【讨论】:

【参考方案5】:

您有两种解决方案:

首先是将枚举器消耗到一个可迭代的集合中(如@skaffman 所述)。正如@Michael 所指出的那样,这将添加 O(n)

如果您可以访问创建枚举器的方法,则可以将其包装到 Iterable 中而无需支付任何费用。在您的情况下,您可以使用以下方法创建 Iterable:

final Request request2 = request;
Iterable<String> headerNamesIterable = new Iterable<String>()
    public Iterator<T>  iterator()
      return Iterators.forEnumeration(request2.getHeaderNames())
    

Iterable<String> headersIterableName1 = new Iterable<String>()
    public Iterator<T>  iterator()
      return Iterators.forEnumeration(request2.getHeaders("name1"))
    

在这种情况下,您可以对由 Iterable 对象创建的迭代器进行多次迭代。

【讨论】:

以上是关于通用枚举到可迭代转换器 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在java中添加集合到可迭代列表[关闭]

在 C# 中将 Int 转换为通用枚举

无法在通用方法中将整数转换为枚举 [重复]

数据结构与算法深入浅出递归和迭代的通用转换思想

递归转换为迭代的一种通用方式

在迭代器中找到第一个特定的枚举变体并对其进行转换