C# Study Notes--Implement a container that can repeat weights and can be automatically sorted--MultiplySortedSet

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);
        }
    }
}

insert image description here

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);
        }
    }
}

insert image description here

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);
        }
    }
}

insert image description here
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.

  1. 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.

  1. 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);
        }
        
    }
}

insert image description here

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);
        }

    }
}

insert image description here

postscript

Custom container – MultiplySortedSet updated a wave

  1. Optimize the logic of the Remove method. (For details, see the annotation of the Remove method in MultiplySortedSet )

Guess you like

Origin blog.csdn.net/qq_52855744/article/details/130582226