JArray - 如何删除未包含在另一个列表中的元素 - 最快/最佳性能方式

时间:2018-02-22 18:10:44

标签: c# json performance json.net

这是我的问题的背景:

有一个JArray有数千个元素。样本中JArray)。

还有另一个只包含数百个元素的小清单。它被称为 umbrellasToBeRemovedIds List<string>)。

我正在尝试这种方法来移除这些遮阳伞:

foreach (string umbrellaToRemoveId in umbrellasToBeRemovedIds)
{
    umbrellas.FirstOrDefault(o => o["Id"].Value<string>() == umbrellaToRemoveId)?.Remove();
}

考虑雨伞ToBeRemovedIds的尺寸小于遮阳伞。 从这个遮阳伞JArray删除元素的最快方法(表现明智)是什么?

1 个答案:

答案 0 :(得分:1)

有关JArray的两个问题需要牢记在性能方面:

  1. JArray没有等效于List<T>.RemoveAll(Predicate<T>)的API,可以使用List<T> reference source中显示的shift-down-and-resize-once算法有效删除多个条目。相反,它有Clear()删除所有项目,RemoveAt()删除单个项目,ReplaceAll()有效地用不同的内容替换当前内容。

    由于已实施的RemoveAt()会从内部List<JToken>中删除条目并将后续列表项向下移动,因此从k大小JArray中移除n项将是O(n*k),这对你的申请来说可能不够好。

  2. JToken层次结构中父母与子女之间存在双向引用:


    因此,JToken不能有两个父母,如果您尝试将已有父母的JToken添加到另一个父母,则克隆< / em>的。即如果arrayJArray,那么

    array[i] = array[i+1];
    

    将克隆索引i+1处的数组条目,而不是简单地将其两次添加到数组中。 (有关发生这种情况的详细信息,请参阅this answer。)

    因此,在应用程序代码中天真地实现shift-down-and-resize-once算法将具有可怕的性能,因为在该过程中克隆了许多数组条目。

  3. 综合以上几点,以下扩展方法在从k大小JArray移除n项目时应具有最佳算法效果:

    public static partial class JTokenExtensions
    {
        /// <summary>
        /// Removes all the elements whose values, as defined by `selector`, belong to the collection of incoming values
        /// </summary>
        public static void RemoveAll<T>(this JArray array, IEnumerable<T> values, Func<JToken, T> selector, IEqualityComparer<T> comparer = null)
        {
            if (array == null || values == null || selector == null)
                throw new ArgumentNullException();
            var set = new HashSet<T>(values, comparer);
            array.RemoveAll(i => set.Contains(selector(i)));
        }
    
        /// <summary>
        /// Removes all the elements that match the conditions defined by the specified predicate.
        /// </summary>
        public static void RemoveAll(this JArray array, Predicate<JToken> match)
        {
            if (array == null || match == null)
                throw new ArgumentNullException();
            array.ReplaceAll(array.Where(i => !match(i)).ToList());
        }
    }
    

    您可以按以下方式致电:

    umbrellas.RemoveAll(umbrellasToBeRemovedIds, i => (string)i["id"]);
    

    这应该具有O(n + k + k*log(k))的性能,其中k*log(k)项是构建哈希值集合的(假定的)复杂性。注意我假设"id"属性是唯一的。如果两个列表中都存在重复的ID,并且您只想从array中删除等于匹配重复ID的数量的项目,则需要更复杂的算法。

    对于潜在的点优化,例如(string)i["id"]i["Id"].Value<string>() 更快,我会引导您阅读Erik Lippert的文章 {{3} }