Table of contents
foreword
Recently, a function needs to be implemented in C#
. There is a container that can automatically sort the elements inside, similar to the priority queue of C++, that is, the heap.
But after querying some containers provided by C#, only SortedSet fits our needs very well.
SortedSet
In fact, it is a container that can be automatically sorted
Let's use it under Unity to see
C# comes with types
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SortedSetTest : MonoBehaviour
{
private void Awake()
{
SortedSet<int> ssi = new SortedSet<int>();
ssi.Add(10);
ssi.Add(1);
ssi.Add(5);
ssi.Add(-1);
foreach (var item in ssi)
{
print(item);
}
}
}
custom class
public class Student : IComparable<Student>
{
private int id;
public Student(int id_in)
{
id = id_in;
}
public int CompareTo(Student other)
{
return id.CompareTo(other.id);
}
public override string ToString()
{
return $"Student ID {
id}";
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SortedSetTest : MonoBehaviour
{
private void Awake()
{
SortedSet<Student> sss = new SortedSet<Student>();
sss.Add(new Student(10));
sss.Add(new Student(1));
sss.Add(new Student(5));
sss.Add(new Student(-1));
foreach (var item in sss)
{
print(item);
}
}
}
SortedSet weights are repeated
We can see that it can sort automatically
But what if the weights are repeated? let's try
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SortedSetTest : MonoBehaviour
{
private void Awake()
{
SortedSet<int> ssi = new SortedSet<int>();
ssi.Add(10);
ssi.Add(1);
ssi.Add(1);
ssi.Add(-1);
foreach (var item in ssi)
{
print(item);
}
}
}
We found that the same 1 was not added to the set, which is also a characteristic of SortedSet , after all, it is a Set .
need
But what we need now is that repeated weights can also be saved and sorted. So what to do?
Here I did not find a container that comes with C# that meets the requirements (C++ has a priority queue), so we customize a container here to achieve it.
Custom container – MultiplySortedSet
It's useless to say more, just go to the code.
using System;
using System.Collections;
using System.Collections.Generic;
namespace Util
{
//排序方法
public enum MultiplySortedType
{
Ascending, //升序
Descending, //降序
}
public class MultiplySortedSet<T> : IEnumerable<T> where T : IComparable<T>
{
private SortedSet<KeyValuePair<T, Guid>> _elements;
private Dictionary<T, Guid> _elementIds;
public MultiplySortedSet(IComparer<KeyValuePair<T, Guid>> comparer = null)
{
//默认为升序
if(comparer == null)
{
comparer = new ComparerAscending();
}
_elements = new SortedSet<KeyValuePair<T, Guid>>(comparer);
_elementIds = new Dictionary<T, Guid>();
}
public MultiplySortedSet(MultiplySortedType sortedType)
{
IComparer<KeyValuePair<T, Guid>> comparer = null;
switch (sortedType)
{
case MultiplySortedType.Ascending:
comparer = new ComparerAscending();
break;
case MultiplySortedType.Descending:
comparer = new ComparerDescending();
break;
}
_elements = new SortedSet<KeyValuePair<T, Guid>>(comparer);
_elementIds = new Dictionary<T, Guid>();
}
public void Add(T element)
{
Guid guid = Guid.NewGuid();
_elements.Add(new KeyValuePair<T, Guid>(element, guid));
_elementIds[element] = guid;
}
public void Remove(T element)
{
//如果Ids存在,则说明_elements里面肯定有该元素
//RemoveWhere是为了删除单一元素
//因为不同T对象的Compare可能相同,但是Guid肯定不同
if (_elementIds.ContainsKey(element))
{
_elements.RemoveWhere(x => x.Key.CompareTo(element) == 0 && x.Value.CompareTo(_elementIds[element]) == 0);
_elementIds.Remove(element);
//更新:上述代码可以替换为下方代码
//{
//Guid id = _elementIds[element];//被删除对象的id
因为_elements在new的时候使用了一个自定义比较器ComparerAscending来初始化
所以即使两个 KeyValuePair<T, Guid> 对象的 Key 属性相等,只要它们的 Value 属性不同,它们也会被视为不同的对象。
所以new一个新的KeyValuePair<T, Guid>对象再去Remove是可以准确删除元素的
//var keyToRemove = new KeyValuePair<T, Guid>(element, id);
//_elements.Remove(keyToRemove);
//}
}
}
public bool Contains(T element)
{
return _elementIds.ContainsKey(element);
}
public int Count
{
get {
return _elements.Count; }
}
public IEnumerator<T> GetEnumerator()
{
foreach (var element in _elements)
{
yield return element.Key;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private class ComparerAscending : IComparer<KeyValuePair<T, Guid>>
{
public int Compare(KeyValuePair<T, Guid> x, KeyValuePair<T, Guid> y)
{
//先调用T的Compare,如果相等就比较Guid
int result = x.Key.CompareTo(y.Key);
if (result == 0)
{
result = x.Value.CompareTo(y.Value);
}
return result;
}
}
private class ComparerDescending : IComparer<KeyValuePair<T, Guid>>
{
public int Compare(KeyValuePair<T, Guid> x, KeyValuePair<T, Guid> y)
{
//先调用T的Compare,如果相等就比较Guid
int result = y.Key.CompareTo(x.Key);
if (result == 0)
{
result = x.Value.CompareTo(y.Value);
}
return result;
}
}
}
}
Core Implementation Ideas
In fact, the core idea is just one more layer of encapsulation. When the object weights are equal, compare another value.
- First of all, SortedSet cannot store the same weight, so we use KeyValuePair to encapsulate one layer here.
Key is the object to be sorted, and the generic type T is used here instead.
The meaning of Value is that when some T objects have equal weights, the value of Value is used for comparison. That is to say, the Value of each object T with equal weight must be different. So here Value can be done with System.Guid.
- Customize a Comparer , if the Key values are equal, then compare the Value values.
Using MultiplySortedSet
C# comes with types
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Victor_Util;
public class Test : MonoBehaviour
{
private void Awake()
{
MultiplySortedSet<int> vs = new MultiplySortedSet<int>();
vs.Add(9);
vs.Add(1);
vs.Add(1);
vs.Add(1);
vs.Add(-5);
vs.Add(100);
foreach (var item in vs)
{
print(item);
}
}
}
custom class
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Victor_Util;
public class Test : MonoBehaviour
{
private void Awake()
{
MultiplySortedSet<Student> vs2 = new MultiplySortedSet<Student>();
vs2.Add(new Student(9));
vs2.Add(new Student(1));
vs2.Add(new Student(1));
vs2.Add(new Student(1));
vs2.Add(new Student(-5));
vs2.Add(new Student(100));
foreach (var item in vs2)
{
print(item);
}
}
}
postscript
Custom container – MultiplySortedSet updated a wave
- Optimize the logic of the Remove method. (For details, see the annotation of the Remove method in MultiplySortedSet )