这是我的问题的背景:
有一个JArray
有数千个元素。样本中伞(JArray
)。
还有另一个只包含数百个元素的小清单。它被称为 umbrellasToBeRemovedIds (List<string>
)。
我正在尝试这种方法来移除这些遮阳伞:
foreach (string umbrellaToRemoveId in umbrellasToBeRemovedIds)
{
umbrellas.FirstOrDefault(o => o["Id"].Value<string>() == umbrellaToRemoveId)?.Remove();
}
考虑雨伞ToBeRemovedIds的尺寸小于遮阳伞。
从这个遮阳伞JArray
删除元素的最快方法(表现明智)是什么?
答案 0 :(得分:1)
有关JArray
的两个问题需要牢记在性能方面:
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)
,这对你的申请来说可能不够好。
JToken
层次结构中父母与子女之间存在双向引用:
JToken.Children()
遍历给定令牌的所有子令牌;
JToken.Parent
获取给定令牌的父级。
因此,JToken
不能有两个父母,如果您尝试将已有父母的JToken
添加到另一个父母,则克隆< / em>的。即如果array
是JArray
,那么
array[i] = array[i+1];
将克隆索引i+1
处的数组条目,而不是简单地将其两次添加到数组中。 (有关发生这种情况的详细信息,请参阅this answer。)
因此,在应用程序代码中天真地实现shift-down-and-resize-once算法将具有可怕的性能,因为在该过程中克隆了许多数组条目。
综合以上几点,以下扩展方法在从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} }