【问题标题】:Does calling IEnumerable.Cast<T>() changes the underlying type to something else, or the underlying type preserved?调用 IEnumerable.Cast<T>() 是否会将基础类型更改为其他类型,或者保留的基础类型?
【发布时间】:2014-04-08 08:44:30
【问题描述】:

假设我有ISet&lt;T&gt; _set = new HashSet&lt;T&gt;();

现在如果我这样做:_set.Cast&lt;TInterface&gt;().Contains(obj, comparer);(其中T 实现TInterface),我会失去O(1)HashSet&lt;T&gt; 的好处吗?

换句话说 - .Cast&lt;T&gt;()ing 是否将基础类型(在本例中为 HashSet&lt;T&gt;)更改为其他类型,或者保留的基础类型?

【问题讨论】:

    标签: c# linq linq-to-objects


    【解决方案1】:

    从逻辑上讲,HashSet&lt;T&gt; 使用基于比较器的哈希逻辑的内部哈希表它是用它创建的,所以当然不可能对它进行元素包含测试它具有 不同的 比较器并期望 O(1) 性能。


    也就是说,让我们更详细地了解一下您的具体场景:

    Cast&lt;T&gt; 方法如下所示(来自参考来源):

      public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) {
                IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
                if (typedSource != null) return typedSource;
                if (source == null) throw Error.ArgumentNull("source");
                return CastIterator<TResult>(source);
            }
    

    如您所见,如果源实现了IEnumerable&lt;TResult&gt;,它只是直接返回源。由于IEnumerable&lt;&gt; 是一个协变接口,这个测试将通过您的用例(假设具体类型实现了接口类型)并且哈希集将直接返回——这是一件好事,因为它的内部哈希表仍有希望正在使用。

    但是,您正在使用的 Contains 的重载如下所示:

     public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
            {
                if (comparer == null) comparer = EqualityComparer<TSource>.Default;
                if (source == null) throw Error.ArgumentNull("source");
                foreach (TSource element in source)
                    if (comparer.Equals(element, value)) return true;
                return false;
            }
    

    如您所见,它总是循环遍历集合进行线性搜索,即 O(n)。

    所以整个操作将是 O(n) 无论如何。

    【讨论】:

      【解决方案2】:

      _set.Cast&lt;TInterface&gt;() 将返回一个IEnumerable&lt;TInterface&gt;,因此_set.Cast&lt;TInterface&gt;().Contains(obj, comparer); 不会调用HashSet.Contains,而是调用Enumerable.Contains 扩展方法。

      所以显然你不会再得到O(1) 操作了。

      如果你需要O(1),你需要再次创建一个HashSet

         var newSet = new HashSet(_set.Cast<TInterface>(),comparer);
         newSet.Contains();
      

      【讨论】:

      • 每次我想测试是否包含某个对象时重新创建新的HashSet 开销很大
      • 但是,如果您想连续测试 1000 个项目,则重新创建 HashSet 是一个更好的选择。
      • 不这么认为:迭代直到找到在 worst 的情况下只有 O(N) (如果你在第二个索引中找到它,那就是 O(2) ..),同时将 N 个项目添加到任何容器中总是 O(N) - 它总是 worst 情况
      • 我认为您误解了我的评论。我说如果你想检查 1000 个项目是否存在于一个序列中,这将是 O(N) * 1000 但如果你只创建一个 HashSet 并检查 1000 个项目,它只是 O(1) * 1000。你明白了吗?
      【解决方案3】:

      Cast 方法返回一个 IEnumerable,因此 Contains 方法将对 IEnumerable 而不是 HashSet 进行操作。所以我认为你会失去 HashSet 的好处。你为什么不在比较中做演员表呢?

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-24
      • 1970-01-01
      • 2019-07-23
      • 2016-11-30
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多