从字典创建一个排序和过滤列表
Create a sorted and filtered list from dictionary
我需要根据未排序的对象字典创建排序和过滤列表。
我尝试维护一个排序的字典,但事实证明这不切实际,因为这些对象包含不断由外部进程更新的属性。
当前的解决方案需要对字典进行两次完整的传递——首先是过滤,然后是排序。我觉得应该有一种方法可以将这些步骤结合起来,以避免对整个集合进行两次循环。
如果可能的话,我正在尝试将下面显示的第一遍和第二遍组合成对字典的一次迭代。
这里速度很重要,所以我怀疑我是否可以使用 Linq。
//SETUP//
//class stub
public class myObject{
public int ID;
public float sortAttribute;
public bool filterAttribute;
}
//unsorted dictionary
Dictionary<int, myObject> myDictionary = new Dictionary<int, myObject>();
//sorting method
public int sortMethod(myObject o1, myObject o2)
{
if (o1.sortAttribute < o2.sortAttribute)
{
return -1;
} else {
return 1;
}
}
//CURRENT PROCESS//
//create empty list
List<myObject> sortedList = new List<myObject>();
//first pass - populate list from dictionary - filtering on the way
foreach (KeyValuePair<int, myObject> entry in myDictionary){
if (entry.Value.filterAttribute){
sortedList.Add(entry.Value)
}
}
//2nd pass - sort the populated list
sortedList.Sort(sortMethod);
由于您没有具体说明您的字典有多大,我将在此处列出 2 个答案和一个建议,一个用于您的字典大时,一个用于您的字典小(足够)时。
让我们从大词典开始。您可以通过多种方式实现这一点,但我会尝试使用 O(n log n) 方法而不是 O(n^2) ,因为您提到速度至关重要。当您创建过滤列表时,我会尝试创建一个有利的合并排序步骤。当您将每个过滤的两个元素添加到新列表时,都可以对它们进行排序。那么就可以跳过归并排序的第一步,直接开始对4个元素进行排序。然而,这几乎与您当前的实现一样快,因为您只跳过合并排序的一个步骤,您将不得不创建自己的合并排序版本,该版本不以 2 个元素开头。
对于小(足够)的字典,您可以使用更快的排序算法。您可以使用计数排序的一个版本来创建可以在 O(n) 中对字典进行排序的过滤器和排序算法。您将需要研究该算法的数据,以了解如何将数据拆分到不同的计数排序桶中,例如如果你所有的数字都在 0 和 1 之间,你可以创建 1000 个桶和 select 一个桶,方法是将浮点数乘以 1000。请注意,如果桶中有多个元素(如果你的数据分割得很好,这些可以非常快,因为不会有很多数字在同一个桶中。
就我个人而言,我建议尝试选项 2,看看它是否能为您带来足够好的性能提升,以及它是否不需要太多的存储桶。如果您需要太多的存储桶或性能不是您想要的,您之后可以随时执行选项 1。
这里你有一个基于排序字典的数据结构,它总是按 SortAttribute
然后按 Id
排序,无论数据是否在外部更改,因为它对数据的更改做出正确反应。
项的插入、修改和删除是 O(log(n))。
免责声明:它不是线程安全的
namespace SortedDataStructureApp
{
public class ValueChangedEventArgs<T> : EventArgs
{
public T? PreviousValue { get; private set; }
public T? CurrentValue { get; private set; }
public ValueChangedEventArgs(T previousValue, T currentValue)
{
this.PreviousValue = previousValue;
this.CurrentValue = currentValue;
}
}
public class MyObject
{
public event EventHandler<ValueChangedEventArgs<int>>? IdChanged;
public event EventHandler<ValueChangedEventArgs<float>>? SortAttributeChanged;
public event EventHandler<ValueChangedEventArgs<bool>>? FilterAttributeChanged;
private int _id;
public int Id
{
get
{
return _id;
}
set
{
if (value == _id) return;
var previousValue = _id;
_id = value;
IdChanged?.Invoke(this, new ValueChangedEventArgs<int>(previousValue, value));
}
}
private float _sortAttribute;
public float SortAttribute
{
get
{
return _sortAttribute;
}
set
{
if (value == _sortAttribute) return;
var previousValue = _sortAttribute;
_sortAttribute = value;
SortAttributeChanged?.Invoke(this, new ValueChangedEventArgs<float>(previousValue, value));
}
}
private bool _filterAttribute;
public bool FilterAttribute
{
get
{
return _filterAttribute;
}
set
{
if (value == _filterAttribute) return;
var previousValue = _filterAttribute;
_filterAttribute = value;
FilterAttributeChanged?.Invoke(this, new ValueChangedEventArgs<bool>(previousValue, value));
}
}
}
internal class SortedDataStructure
{
private Dictionary<int, MyObject> unsortedDictionary = new Dictionary<int, MyObject>();
private SortedDictionary<float, SortedDictionary<int, MyObject>> sortedDictionary = new SortedDictionary<float, SortedDictionary<int, MyObject>>();
private EventHandler<ValueChangedEventArgs<int>> idChangedHandler;
private EventHandler<ValueChangedEventArgs<float>> sortAttributeChangedHandler;
private EventHandler<ValueChangedEventArgs<bool>> filterAttributeChangedHandler;
public SortedDataStructure()
{
idChangedHandler = new EventHandler<ValueChangedEventArgs<int>>(IdChanged);
sortAttributeChangedHandler = new EventHandler<ValueChangedEventArgs<float>>(SortAttributeChanged);
filterAttributeChangedHandler = new EventHandler<ValueChangedEventArgs<bool>>(FilterAttributeChanged);
}
private void IdChanged(object? sender, ValueChangedEventArgs<int> e)
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
var obj = (MyObject)sender;
Remove(e.PreviousValue);
Add(obj);
}
private void SortAttributeChanged(object? sender, ValueChangedEventArgs<float> e)
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
var obj = (MyObject)sender;
if (obj.FilterAttribute)
{
RemoveFromSortedDictionary(e.PreviousValue, obj.Id);
AddToSortedDictionary(obj);
}
}
private void FilterAttributeChanged(object? sender, ValueChangedEventArgs<bool> e)
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
var obj = (MyObject)sender;
if (e.CurrentValue == true)
{
AddToSortedDictionary(obj);
}
else
{
RemoveFromSortedDictionary(obj.SortAttribute, obj.Id);
}
}
public void Add(MyObject obj)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
unsortedDictionary.Add(obj.Id, obj);
if (obj.FilterAttribute)
{
AddToSortedDictionary(obj);
}
obj.FilterAttributeChanged += this.filterAttributeChangedHandler;
obj.IdChanged += this.idChangedHandler;
obj.SortAttributeChanged += this.sortAttributeChangedHandler;
}
private void RemoveFromSortedDictionary(float sortAttribute, int id)
{
if (sortedDictionary.TryGetValue(sortAttribute, out var subDictionary) == false)
{
throw new InvalidOperationException($"Sorted structure is corrupted. Could not find sortAttribute: {sortAttribute}");
}
if (subDictionary.Remove(id) == false)
{
throw new InvalidOperationException($"Sorted structure is corrupted. Could not find id {id} in subdictionary");
}
}
private void AddToSortedDictionary(MyObject obj)
{
if (sortedDictionary.TryGetValue(obj.SortAttribute, out var subDictionary) == false)
{
subDictionary = new SortedDictionary<int, MyObject>();
sortedDictionary.Add(obj.SortAttribute, subDictionary);
}
subDictionary.Add(obj.Id, obj);
}
public bool Remove(int id)
{
if (unsortedDictionary.Remove(id, out var obj) == false)
{
return false;
}
if (obj.FilterAttribute)
{
RemoveFromSortedDictionary(obj.SortAttribute, id);
}
obj.FilterAttributeChanged -= this.filterAttributeChangedHandler;
obj.IdChanged -= this.idChangedHandler;
obj.SortAttributeChanged -= this.sortAttributeChangedHandler;
return true;
}
public IEnumerable<MyObject> GetSortedValues()
{
return this.sortedDictionary.Values.SelectMany(x => x.Values);
}
public MyObject GetById(int id)
{
return unsortedDictionary[id];
}
public bool ContainsKey(int id)
{
return unsortedDictionary.ContainsKey(id);
}
public bool TryGetValue(int id, out MyObject? obj)
{
return unsortedDictionary.TryGetValue(id, out obj);
}
}
}
显示其工作原理的代码:
using SortedDataStructureApp;
var obj1 = new MyObject
{
Id = 1,
SortAttribute = 1,
FilterAttribute = true
};
var obj2 = new MyObject
{
Id = 2,
SortAttribute = 1,
FilterAttribute = true
};
var data = new SortedDataStructure();
data.Add(obj1);
data.Add(obj2);
// It shows 1, 2. Both have the same sort attribute but Id = 1 comes before Id = 2
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj1.Id = 3;
Console.WriteLine();
// It shows 2, 3
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj1.SortAttribute = 0.5f;
Console.WriteLine();
// obj1.SortAttribute < obj2.SortAttribute, so it shows 3, 2
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj2.FilterAttribute = false;
Console.WriteLine();
// obj2 doesn't appear
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj2.FilterAttribute = true;
obj1.FilterAttribute = false;
// now only obj2 is shown
Console.WriteLine();
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
我需要根据未排序的对象字典创建排序和过滤列表。
我尝试维护一个排序的字典,但事实证明这不切实际,因为这些对象包含不断由外部进程更新的属性。
当前的解决方案需要对字典进行两次完整的传递——首先是过滤,然后是排序。我觉得应该有一种方法可以将这些步骤结合起来,以避免对整个集合进行两次循环。
如果可能的话,我正在尝试将下面显示的第一遍和第二遍组合成对字典的一次迭代。
这里速度很重要,所以我怀疑我是否可以使用 Linq。
//SETUP//
//class stub
public class myObject{
public int ID;
public float sortAttribute;
public bool filterAttribute;
}
//unsorted dictionary
Dictionary<int, myObject> myDictionary = new Dictionary<int, myObject>();
//sorting method
public int sortMethod(myObject o1, myObject o2)
{
if (o1.sortAttribute < o2.sortAttribute)
{
return -1;
} else {
return 1;
}
}
//CURRENT PROCESS//
//create empty list
List<myObject> sortedList = new List<myObject>();
//first pass - populate list from dictionary - filtering on the way
foreach (KeyValuePair<int, myObject> entry in myDictionary){
if (entry.Value.filterAttribute){
sortedList.Add(entry.Value)
}
}
//2nd pass - sort the populated list
sortedList.Sort(sortMethod);
由于您没有具体说明您的字典有多大,我将在此处列出 2 个答案和一个建议,一个用于您的字典大时,一个用于您的字典小(足够)时。
让我们从大词典开始。您可以通过多种方式实现这一点,但我会尝试使用 O(n log n) 方法而不是 O(n^2) ,因为您提到速度至关重要。当您创建过滤列表时,我会尝试创建一个有利的合并排序步骤。当您将每个过滤的两个元素添加到新列表时,都可以对它们进行排序。那么就可以跳过归并排序的第一步,直接开始对4个元素进行排序。然而,这几乎与您当前的实现一样快,因为您只跳过合并排序的一个步骤,您将不得不创建自己的合并排序版本,该版本不以 2 个元素开头。
对于小(足够)的字典,您可以使用更快的排序算法。您可以使用计数排序的一个版本来创建可以在 O(n) 中对字典进行排序的过滤器和排序算法。您将需要研究该算法的数据,以了解如何将数据拆分到不同的计数排序桶中,例如如果你所有的数字都在 0 和 1 之间,你可以创建 1000 个桶和 select 一个桶,方法是将浮点数乘以 1000。请注意,如果桶中有多个元素(如果你的数据分割得很好,这些可以非常快,因为不会有很多数字在同一个桶中。
就我个人而言,我建议尝试选项 2,看看它是否能为您带来足够好的性能提升,以及它是否不需要太多的存储桶。如果您需要太多的存储桶或性能不是您想要的,您之后可以随时执行选项 1。
这里你有一个基于排序字典的数据结构,它总是按 SortAttribute
然后按 Id
排序,无论数据是否在外部更改,因为它对数据的更改做出正确反应。
项的插入、修改和删除是 O(log(n))。
免责声明:它不是线程安全的
namespace SortedDataStructureApp
{
public class ValueChangedEventArgs<T> : EventArgs
{
public T? PreviousValue { get; private set; }
public T? CurrentValue { get; private set; }
public ValueChangedEventArgs(T previousValue, T currentValue)
{
this.PreviousValue = previousValue;
this.CurrentValue = currentValue;
}
}
public class MyObject
{
public event EventHandler<ValueChangedEventArgs<int>>? IdChanged;
public event EventHandler<ValueChangedEventArgs<float>>? SortAttributeChanged;
public event EventHandler<ValueChangedEventArgs<bool>>? FilterAttributeChanged;
private int _id;
public int Id
{
get
{
return _id;
}
set
{
if (value == _id) return;
var previousValue = _id;
_id = value;
IdChanged?.Invoke(this, new ValueChangedEventArgs<int>(previousValue, value));
}
}
private float _sortAttribute;
public float SortAttribute
{
get
{
return _sortAttribute;
}
set
{
if (value == _sortAttribute) return;
var previousValue = _sortAttribute;
_sortAttribute = value;
SortAttributeChanged?.Invoke(this, new ValueChangedEventArgs<float>(previousValue, value));
}
}
private bool _filterAttribute;
public bool FilterAttribute
{
get
{
return _filterAttribute;
}
set
{
if (value == _filterAttribute) return;
var previousValue = _filterAttribute;
_filterAttribute = value;
FilterAttributeChanged?.Invoke(this, new ValueChangedEventArgs<bool>(previousValue, value));
}
}
}
internal class SortedDataStructure
{
private Dictionary<int, MyObject> unsortedDictionary = new Dictionary<int, MyObject>();
private SortedDictionary<float, SortedDictionary<int, MyObject>> sortedDictionary = new SortedDictionary<float, SortedDictionary<int, MyObject>>();
private EventHandler<ValueChangedEventArgs<int>> idChangedHandler;
private EventHandler<ValueChangedEventArgs<float>> sortAttributeChangedHandler;
private EventHandler<ValueChangedEventArgs<bool>> filterAttributeChangedHandler;
public SortedDataStructure()
{
idChangedHandler = new EventHandler<ValueChangedEventArgs<int>>(IdChanged);
sortAttributeChangedHandler = new EventHandler<ValueChangedEventArgs<float>>(SortAttributeChanged);
filterAttributeChangedHandler = new EventHandler<ValueChangedEventArgs<bool>>(FilterAttributeChanged);
}
private void IdChanged(object? sender, ValueChangedEventArgs<int> e)
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
var obj = (MyObject)sender;
Remove(e.PreviousValue);
Add(obj);
}
private void SortAttributeChanged(object? sender, ValueChangedEventArgs<float> e)
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
var obj = (MyObject)sender;
if (obj.FilterAttribute)
{
RemoveFromSortedDictionary(e.PreviousValue, obj.Id);
AddToSortedDictionary(obj);
}
}
private void FilterAttributeChanged(object? sender, ValueChangedEventArgs<bool> e)
{
if (sender == null) throw new ArgumentNullException(nameof(sender));
var obj = (MyObject)sender;
if (e.CurrentValue == true)
{
AddToSortedDictionary(obj);
}
else
{
RemoveFromSortedDictionary(obj.SortAttribute, obj.Id);
}
}
public void Add(MyObject obj)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
unsortedDictionary.Add(obj.Id, obj);
if (obj.FilterAttribute)
{
AddToSortedDictionary(obj);
}
obj.FilterAttributeChanged += this.filterAttributeChangedHandler;
obj.IdChanged += this.idChangedHandler;
obj.SortAttributeChanged += this.sortAttributeChangedHandler;
}
private void RemoveFromSortedDictionary(float sortAttribute, int id)
{
if (sortedDictionary.TryGetValue(sortAttribute, out var subDictionary) == false)
{
throw new InvalidOperationException($"Sorted structure is corrupted. Could not find sortAttribute: {sortAttribute}");
}
if (subDictionary.Remove(id) == false)
{
throw new InvalidOperationException($"Sorted structure is corrupted. Could not find id {id} in subdictionary");
}
}
private void AddToSortedDictionary(MyObject obj)
{
if (sortedDictionary.TryGetValue(obj.SortAttribute, out var subDictionary) == false)
{
subDictionary = new SortedDictionary<int, MyObject>();
sortedDictionary.Add(obj.SortAttribute, subDictionary);
}
subDictionary.Add(obj.Id, obj);
}
public bool Remove(int id)
{
if (unsortedDictionary.Remove(id, out var obj) == false)
{
return false;
}
if (obj.FilterAttribute)
{
RemoveFromSortedDictionary(obj.SortAttribute, id);
}
obj.FilterAttributeChanged -= this.filterAttributeChangedHandler;
obj.IdChanged -= this.idChangedHandler;
obj.SortAttributeChanged -= this.sortAttributeChangedHandler;
return true;
}
public IEnumerable<MyObject> GetSortedValues()
{
return this.sortedDictionary.Values.SelectMany(x => x.Values);
}
public MyObject GetById(int id)
{
return unsortedDictionary[id];
}
public bool ContainsKey(int id)
{
return unsortedDictionary.ContainsKey(id);
}
public bool TryGetValue(int id, out MyObject? obj)
{
return unsortedDictionary.TryGetValue(id, out obj);
}
}
}
显示其工作原理的代码:
using SortedDataStructureApp;
var obj1 = new MyObject
{
Id = 1,
SortAttribute = 1,
FilterAttribute = true
};
var obj2 = new MyObject
{
Id = 2,
SortAttribute = 1,
FilterAttribute = true
};
var data = new SortedDataStructure();
data.Add(obj1);
data.Add(obj2);
// It shows 1, 2. Both have the same sort attribute but Id = 1 comes before Id = 2
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj1.Id = 3;
Console.WriteLine();
// It shows 2, 3
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj1.SortAttribute = 0.5f;
Console.WriteLine();
// obj1.SortAttribute < obj2.SortAttribute, so it shows 3, 2
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj2.FilterAttribute = false;
Console.WriteLine();
// obj2 doesn't appear
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}
obj2.FilterAttribute = true;
obj1.FilterAttribute = false;
// now only obj2 is shown
Console.WriteLine();
foreach (var obj in data.GetSortedValues())
{
Console.WriteLine(obj.Id);
}