linq 如何有效地检查IEnumerable是否具有单个元素?

tv6aics1  于 2022-12-06  发布在  其他
关注(0)|答案(7)|浏览(172)

Count()扫描所有元素,因此如果enumerable包含很多元素,if (list.Count() == 1)将不能很好地执行。
Single()会在并非只有一个元素时掷回例外状况。使用try { list.Single(); } catch(InvalidOperationException e) {}会很笨拙且效率不高。
如果有一个以上的项目,SingleOrDefault()会掷回例外状况,因此if (list.SingleOrDefault() == null)(假设TSource是指涉型别)将无法用于大小大于1的可枚举项目。

hts6caw3

hts6caw31#

var exactlyOne = sequence.Take(2).Count() == 1;

如果元素较少,Take扩展方法不会抛出,它只返回可用的元素。

siotufzp

siotufzp2#

更直接:

public static bool HasSingle<T>(this IEnumerable<T> sequence) {
    if (sequence is ICollection<T> list) return list.Count == 1; // simple case
    using(var iter = sequence.GetEnumerator()) {
        return iter.MoveNext() && !iter.MoveNext();
    }
}

但是请注意,您只能保证一次读取序列,因此 * 在这些情况下 *:通过简单的检查是否有一个单一的项目,你就不能再得到这个项目了。2所以你可能更喜欢一些如果有价值的东西:

public static bool HasSingle<T>(this IEnumerable<T> sequence, out T value)
{
    if (sequence is IList<T> list)
    {
        if(list.Count == 1)
        {
            value = list[0];
            return true;
        }
    }
    else
    {
        using (var iter = sequence.GetEnumerator())
        {
            if (iter.MoveNext())
            {
                value = iter.Current;
                if (!iter.MoveNext()) return true;
            }
        }
    }

    value = default(T);
    return false;
}
2jcobegt

2jcobegt3#

为了避免其他答案中的额外迭代,您可以实现自己的扩展:

public static bool HasExactlyOneElement<T>(this IEnumerable<T> source)
{
    using (var enumerator = source.GetEnumerator())
        return enumerator.MoveNext() && !enumerator.MoveNext();
}
guykilcj

guykilcj4#

您可以使用!Skip(1).Any()

bool contains1 = items.Any() && !items.Skip(1).Any();

如果类型是集合,您可以建立更有效率的延伸:

public static bool ContainsCountItems<TSource>(this IEnumerable<TSource> source, int count)
{
    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count == count;
    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count == count;
    int itemCount = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext() && ++itemCount <= count)
            {
                if (itemCount == count)
                    return !e.MoveNext();
            }
        }
    }
    return false;
}

用法:

var items = Enumerable.Range(0, 1);
bool contains1 = items.ContainsCountItems(1); // true;
items = Enumerable.Range(0, 2);
contains1 = items.ContainsCountItems(1); // false;

您可以将此扩展名用于任何类型和任何计数,因此不仅1

var items = Enumerable.Range(0, 10);
bool contains10 = items.ContainsCountItems(10); // true;
izkcnapc

izkcnapc5#

我建议玩Any,我们必须检查一下

  1. list至少有一个项目-Any
  2. list没有第二个项目-!list.Skip(1).Any()
    编码:
bool isSingle = list.Any() && !list.Skip(1).Any();

然而,这种方法具有缺点:它扫描list * 两次 *,这在IQueryable的情况下可能是个问题(查询执行 * 两次 *,结果可能不同,并产生额外开销)

mznpcxlj

mznpcxlj6#

您在原始可枚举对象上调用的每个Linq方法调用(.Any().Skip()、...)都会创建一个枚举数,根据您的要求,这也可能会对性能造成相当大的影响。
所以你可以用.Take(2).Count() == 1
另请参阅There's a most performant way to check that a collection has exactly 1 element?

fv2wmkja

fv2wmkja7#

如果出于某种原因不想使用Linq,也可以手动使用Enumerator

/// <summary>
/// Checks that the IEnumerable&lt;T&gt; has exactly one item
/// </summary>
public static bool HasSingleElement<T>(IEnumerable<T> value)
{
    using ( var enumerator = value.GetEnumerator() )
    {
        // Try to get first element - return false if that doesn't exist
        if ( !enumerator.MoveNext() )
            return false;

        // Try to get second element - return false if it does exist
        if ( enumerator.MoveNext() )
            return false;

        // exactly one element exists
        return true;
    }
}

相关问题