【问题标题】:How can I efficiently determine if an IEnumerable has more than one element?如何有效地确定 IEnumerable 是否具有多个元素?
【发布时间】:2013-06-21 15:17:33
【问题描述】:

给定一个初始化的IEnumerable:

IEnumerable<T> enumerable;

我想确定它是否包含多个元素。我认为最明显的方法是:

enumerable.Count() > 1

但是,我相信Count() 枚举了 整个 集合,这对于这个用例来说是不必要的。例如,如果集合包含大量元素或从外部来源提供其数据,则这在性能方面可能会非常浪费。

如何在不枚举超过 2 个元素的情况下做到这一点?

【问题讨论】:

  • 如果你必须依靠它,然后做点什么,你最好强迫它评估成一个列表或其他集合。
  • @devshorts,依靠它,你的意思是依靠它工作还是使用Count()方法?
  • 我的意思是如果你需要找出它上面有多少元素,然后根据它做一些事情,你不妨评估一下。 Count 将评估一次,然后如果您再次迭代它,您必须再次评估所有内容。与任何其他IEnumerable 方法相同,例如TakeSkip。这真的取决于数据是什么。
  • @devshorts,谢谢;我同意不要重复列举很重要。但是,请记住,在某些情况下,您可能只关心这个单一标准并且不想对可枚举做任何其他工作。
  • 如果底层IEnumerableArrayICollectionIQueryable,您可能要考虑坚持使用Count(),因为Count() 已优化以检测这些并使用长度/Count 属性视情况而定。

标签: c# vb.net linq


【解决方案1】:

您可以通过组合 System.Linq 中的扩展方法以多种方式对此进行测试...下面是两个简单的示例:

bool twoOrMore = enumerable.Skip(1).Any();
bool twoOrMoreOther = enumerable.Take(2).Count() == 2;

我更喜欢第一个,因为这是检查Count() &gt;= 1 是否与Any() 一起使用的常用方法,因此我觉得它更具可读性。

【讨论】:

  • 这并不总是有效的,如果这是一个数据流,您已经丢失了您跳过和/或获取的数据。此外,如果每次您对可枚举进行操作时数据都会发生变化,这意味着您在完成此测试后对数据的评估可能会有所不同。
  • @devshorts 但他只关心计数,而不关心实际数据。
  • @devshorts 是的。当然,如有必要,您可以将 Take(2) 结果保存到变量中,但这不是问题中的要求。
  • @DennisRongo,你是对的,但他也在询问 ienumerable 是否是大型数据源或外部流的基础,在这种情况下,对它进行 take/skip 操作会丢失数据.现在您需要创建一个新的可枚举来为您获取的数据添加前缀。可能,只是丑陋。
  • @devshorts,通过枚举至少 2 个元素,在不丢失数据(在您的意义上)的情况下是否不可能满足我的标准?
【解决方案2】:

为了好玩,调用 Next() 两次,然后获取另一个 IEnumerable。

或者,为此特定目标编写一个小型包装类:EnumerablePrefetcher : IEnumerable&lt;T&gt; 以尝试在初始化时获取指定数量的项目。

它的IEnumerable&lt;T&gt; GetItems() 方法应该以这种方式使用yield return

foreach (T item in prefetchedItems) // array of T, prefetched and decided if IEnumerable has at least n elements
{
  yield return item;
}
foreach (T item in otherItems) // IEnumerable<T>
{
  yield return item;
}

【讨论】:

  • 谢谢;我没想过要进行预取或缓存IEnumerable
  • 代码示例可以表示为return prefetchedItems.Concat(otherItems);,虽然上面的也是实心的。
  • @CameronS,我才意识到Concat的存在;谢谢!我原以为框架中缺少这样的功能。
【解决方案3】:

@Cameron-S 的解决方案更简单,但以下更有效。我是根据Enumerable.Count() 方法提出的。 Skip() 将始终迭代而不是短路以获取 sourceICollectionICollection&lt;T&gt; 类型的计数。

/// <summary>
/// Returns true if source has at least <paramref name="count"/> elements efficiently.
/// </summary>
/// <remarks>Based on int Enumerable.Count() method.</remarks>
public static bool HasCountOfAtLeast<TSource>(this IEnumerable<TSource> source, int count)
{
    source.ThrowIfArgumentNull("source");
    var collection = source as ICollection<TSource>;
    if (collection != null)
    {
        return collection.Count >= count;
    }
    var collection2 = source as ICollection;
    if (collection2 != null)
    {
        return collection2.Count >= count;
    }
    int num = 0;
    checked
    {
        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                num++;
                if (num >= count)
                {
                    return true;
                }
            }
        }
    }
    // returns true for source with 0 elements and count 0
    return num == count;
}

【讨论】:

    【解决方案4】:

    我也有类似的需求,但如果 IEnumerable 只有一个值,则从它获取单个值。我为它做了一个扩展方法:

    public static S OneOnlyOrDefault<S>(this IEnumerable<S> items)
    {
        var rtn = default(S);
        using (var en = items.GetEnumerator())
        {
            if (en.MoveNext())
            {
                rtn = en.Current;
            }
            if (en.MoveNext())
            {
                rtn = default(S);
            }
        }
        return rtn;
    }
    

    回答这个问题这个集合是否只包含一项?你可以这样做(在这种情况下集合包含引用类型):

    if (myList.OneOnlyOrDefault() == null)
    {
        // list is either empty or contains more than one item
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-20
      • 2013-01-02
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-08
      • 2011-02-15
      • 1970-01-01
      相关资源
      最近更新 更多