【问题标题】:How to sort the list with duplicate keys?如何使用重复键对列表进行排序?
【发布时间】:2010-08-05 12:15:39
【问题描述】:

我有一组从两个不同的配置文件中读取的元素/键。因此,键可能相同,但与每个键关联的值不同。

我想按排序顺序列出它们。我能做些什么 ?我尝试使用 SortedList 类,但它不允许重复键。

我该怎么做?

例如,假设我有 3 个元素,键为 1、2、3。然后我又得到一个具有键 2(但值不同)的元素。然后我希望在现有密钥 2 之后但在 3 之前插入新密钥。如果我再次找到具有密钥 2 的元素,那么它应该在最近添加的密钥 2 之后。

请注意,我使用的是 .NET 2.0

【问题讨论】:

  • 你真的关心等键的元素是在现有元素之前还是之后?
  • 是的。我想保持问题中提到的顺序

标签: c# data-structures .net-2.0 generic-list


【解决方案1】:

我更喜欢将 LINQ 用于此类事情:

using System.Linq;

...

var mySortedList = myList.Orderby(l => l.Key)
                         .ThenBy(l => l.Value);

foreach (var sortedItem in mySortedList) {
    //You'd see each item in the order you specified in the loop here.
}

注意:您必须使用 .NET 3.5 或更高版本才能完成此操作。

【讨论】:

  • 谢谢,但我使用的是 .NET 2.0
  • 呸。仅此一项就足以升级。
  • 这在使用 .net 2.0 时是不可能的,所以不能回答他的问题
  • @nealv:你可能想看看问题的编辑历史。您可能会注意到此信息最初不在问题文本中。
  • 我以为您希望新添加的具有相同值的键位于列表中已有的键之后。这不会那样做。这将根据值对具有相同键值的项目进行排序。
【解决方案2】:

您需要的是带有自定义IComparer 的排序函数。您现在拥有的是使用 sort 时的默认 icomparer。这将检查字段值。

当您创建自定义 IComparer 时(通过实现 Icomparable 接口在您的类中执行此操作)。它的作用是:您的对象会检查您排序列表中的所有其他对象。

这是由一个函数完成的。 (不用担心 VS 会在引用你的界面时实现它

public class  ThisObjectCLass : IComparable{

    public int CompareTo(object obj) {
            ThisObjectCLass something = obj as ThisObjectCLass ;
            if (something!= null) 
                if(this.key.CompareTo(object.key) == 0){
                //then:
                   if .....
                }
                else if(this.value "is more important then(use some logic here)" something.value){
                 return 1
                }
                else return -1
            else
               throw new ArgumentException("I am a dumb little rabid, trying to compare different base classes");
        }
}

阅读上面的链接以获得更好的信息。

我知道一开始我自己在理解这一点时遇到了一些麻烦,所以如果有任何额外的帮助,请添加评论,我会详细说明

【讨论】:

    【解决方案3】:

    我通过创建SortedList<int, List<string>> 来做到这一点。每当我找到重复键时,我只需将值插入与已存在于 SortedList 对象中的键关联的现有列表中。这样,我可以获得特定键的值列表。

    【讨论】:

    • @BlueRaja-DannyPflughoeft:有一个SortedList,但它不允许重复键。请注意,我的问题是针对 .NET 2.0 的。无论如何,从 .NET 3.5 开始,同样的问题可以使用 Linq 中的 Lookup 来解决。请参阅此链接 - msdn.microsoft.com/en-us/library/bb460184.aspx
    • 我知道 SortedList 和 Lookup。但这些都是地图,而不是列表。 C# 中没有实际的排序列表。有List.Sort(),但插入然后排序列表是O(n log n) 操作,而在最坏的情况下应该只是O(log n)O(n)
    【解决方案4】:

    使用您自己的比较器类! 如果排序列表中的键是整数,则可以使用例如此比较器:

    public class DegreeComparer : IComparer<int>
    {
        #region IComparer<int> Members
    
        public int Compare(int x, int y)
        {
            if (x < y)
                return -1;
            else
                return 1;
        }
    
        #endregion
    }
    

    要使用 int 键和字符串值实例化一个新的 SortedList,请使用:

    var mySortedList = new SortedList<int, string>(new DegreeComparer());
    

    【讨论】:

      【解决方案5】:

      如果你真的不关心等键元素的顺序,将所有内容添加到列表中,然后按键排序:

      static void Main(string[] args)
      {
         List<KeyValuePair<int, MyClass>> sortedList = 
            new List<KeyValuePair<int, MyClass>>() {
               new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
               new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
               new KeyValuePair<int, MyClass>(5, new MyClass("five")),
               new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
               new KeyValuePair<int, MyClass>(7, new MyClass("seven-b"))
            };
         sortedList.Sort(Compare);
      }
      static int Compare(KeyValuePair<int, MyClass> a, KeyValuePair<int, MyClass> b)
      {
         return a.Key.CompareTo(b.Key);
      }
      

      如果您确实希望稍后插入的项目位于较早插入的项目之后,请在插入时对其进行排序:

      class Sorter : IComparer<KeyValuePair<int, MyClass>>
      {
      
      static void Main(string[] args)
      {
         List<KeyValuePair<int, MyClass>> sortedList = new List<KeyValuePair<int, MyClass>>();
         Sorter sorter = new Sorter();
         foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] {
            new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
            new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
            new KeyValuePair<int, MyClass>(5, new MyClass("five")),
            new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
            new KeyValuePair<int, MyClass>(4, new MyClass("four-c")),
            new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) })
         {
            sorter.Insert(sortedList, kv);
         }
         for (int i = 0; i < sortedList.Count; i++)
         {
            Console.WriteLine(sortedList[i].ToString());
         }
      }
      void Insert(List<KeyValuePair<int, MyClass>> sortedList, KeyValuePair<int, MyClass> newItem)
      {
         int newIndex = sortedList.BinarySearch(newItem, this);
         if (newIndex < 0)
            sortedList.Insert(~newIndex, newItem);
         else
         {
            while (newIndex < sortedList.Count && (sortedList[newIndex].Key == newItem.Key))
               newIndex++;
            sortedList.Insert(newIndex, newItem);
         }
      }
      #region IComparer<KeyValuePair<int,MyClass>> Members
      
      public int Compare(KeyValuePair<int, MyClass> x, KeyValuePair<int, MyClass> y)
      {
         return x.Key.CompareTo(y.Key);
      }
      
      #endregion
      }
      

      或者你可以有一个排序列表:

      static void Main(string[] args)
      {
         SortedDictionary<int, List<MyClass>> sortedList = new SortedDictionary<int,List<MyClass>>();
         foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] {
            new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
            new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
            new KeyValuePair<int, MyClass>(5, new MyClass("five")),
            new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
            new KeyValuePair<int, MyClass>(4, new MyClass("four-c")),
            new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) })
         {
            List<MyClass> bucket;
            if (!sortedList.TryGetValue(kv.Key, out bucket))
               sortedList[kv.Key] = bucket = new List<MyClass>();
            bucket.Add(kv.Value);
         }
         foreach(KeyValuePair<int, List<MyClass>> kv in sortedList)
         {
            for (int i = 0; i < kv.Value.Count; i++ )
               Console.WriteLine(kv.Value[i].ToString());
         }
      }
      

      我不确定您是否可以像我在上面第一个示例中所做的那样在 .NET 2.0 中使用 List 初始化程序,但我确定您知道如何使用数据填充列表。

      【讨论】:

        【解决方案6】:

        .NET 对稳定排序 没有很大的支持(这意味着等价元素在排序时保持它们的相对顺序)。但是,您可以使用List.BinarySearch 和自定义IComparer&lt;T&gt; 编写自己的稳定排序插入(如果键小于或等于目标,则返回-1,如果更大)。

        请注意,List.Sort 不是稳定排序,因此您要么必须编写自己的稳定快速排序例程,要么只使用插入排序来初始填充集合。

        【讨论】:

          【解决方案7】:

          您是否考虑过 NameValueCollection 类,因为它允许您为每个键存储多个值?例如,您可以拥有以下内容:

              NameValueCollection nvc = new NameValueCollection();
              nvc.Add("1", "one");
              nvc.Add("2", "two");
              nvc.Add("3", "three");
          
              nvc.Add("2", "another value for two");
              nvc.Add("1", "one bis");
          

          然后检索您可能拥有的值:

              for (int i = 0; i < nvc.Count; i++)
              {
                  if (nvc.GetValues(i).Length > 1)
                  {
                      for (int x = 0; x < nvc.GetValues(i).Length; x++)
                      {
                          Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i).GetValue(x));
                      }
                  }
                  else
                  {
                      Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i)[0]);
                  }
          
              }
          

          给出输出:

          '1' = '一个'

          '1' = '一二'

          '2' = '两个'

          '2' = '两个的另一个值'

          '3' = '三个'

          【讨论】:

            【解决方案8】:

            在 .NET 2.0 中,您可以编写:

            List<KeyValuePair<string, string>> keyValueList = new List<KeyValuePair<string, string>>();
            
            // Simulate your list of key/value pair which key could be duplicate
            keyValueList.Add(new KeyValuePair<string,string>("1","One"));
            keyValueList.Add(new KeyValuePair<string,string>("2","Two"));
            keyValueList.Add(new KeyValuePair<string,string>("3","Three"));
            
            // Here an entry with duplicate key and new value
            keyValueList.Add(new KeyValuePair<string, string>("2", "NEW TWO")); 
            
            // Your final sorted list with one unique key
            SortedList<string, string> sortedList = new SortedList<string, string>();
            
            foreach (KeyValuePair<string, string> s in keyValueList)
            {
                // Use the Indexer instead of Add method
                sortedList[s.Key] = s.Value;
            }
            

            输出:

            [1, One]
            [2, NEW TWO]
            [3, Three]
            

            【讨论】:

              【解决方案9】:

              这个怎么样

                      SortedList<string, List<string>> sl = new SortedList<string, List<string>>();
              
                      List<string> x = new List<string>();
              
                      x.Add("5");
                      x.Add("1");
                      x.Add("5");
                      // use this to load  
                      foreach (string z in x)
                      {
                          if (!sl.TryGetValue(z, out x))
                          {
                              sl.Add(z, new List<string>());
                          }
              
                          sl[z].Add("F"+z);
                      }
                      // use this to print 
                      foreach (string key in sl.Keys)
                      {
                          Console.Write("key=" + key + Environment.NewLine);
              
                          foreach (string item in sl[key])
                          {
                              Console.WriteLine(item);
                          }
                      }
              

              【讨论】:

              • 感谢您查看问题。但是SortedList 没有用。正如问题本身所提到的,由于某种原因,我会有重复的键,SortedList 不允许重复键。
              • @CSharpLearner,这个答案不使用重复键。具有重复键的项目被添加到列表中,因此如果您遍历键,您将获得唯一键的列表。对于任何键,您可能有 1 个或多个值,因为每个值本身就是一个列表。这意味着它是值列表的 SortedList。
              【解决方案10】:

              我有一个类似的问题,我正在设计一个类似于国际象棋游戏概念的游戏,你让计算机进行移动。我需要有多个棋子能够移动的可能性,因此我需要有多个棋盘状态。每个 BoardState 都需要根据棋子的位置进行排名。为了论证和简单起见,假设我的游戏是 Noughts and Crosses,而我是 Noughts,而计算机是 Crosses。如果棋盘状态显示连续 3 个 Noughts,那么这对我来说是最好的状态,如果它显示连续 3 个 Crosses,那么这对我来说是最差的状态,对计算机来说是最好的状态。游戏中还有其他状态对其中一种或另一种更有利,而且还有多种状态会导致平局,所以当排名分数相等时,我该如何对其进行排名。这是我想出的(如果您不是VB程序员,请提前道歉)。

              我的比较类:

              Class ByRankScoreComparer
                  Implements IComparer(Of BoardState)
              
                  Public Function Compare(ByVal bs1 As BoardState, ByVal bs2 As BoardState) As Integer Implements IComparer(Of BoardState).Compare
                      Dim result As Integer = bs2.RankScore.CompareTo(bs1.RankScore) 'DESCENDING order
                      If result = 0 Then
                          result = bs1.Index.CompareTo(bs2.Index)
                      End If
                      Return result
                  End Function
              End Class
              

              我的声明:

              Dim boardStates As SortedSet(Of BoardState)(New ByRankScoreComparer)
              

              我的董事会状态实施:

              Class BoardState
                  Private Shared BoardStateIndex As Integer = 0
                  Public ReadOnly Index As Integer
                  ...
                  Public Sub New ()
                      BoardStateIndex += 1
                      Index = BoardStateIndex
                  End Sub
                  ...
              End Class
              

              正如你所看到的,RankScores 是按降序维护的,任何 2 个具有相同 rank-score 的状态都会排在最后,因为它总是有一个更大的分配索引,因此这允许重复。我还可以安全地调用 boardStates.Remove(myCurrentBoardState) ,它也使用比较器,比较器必须返回 0 值才能找到要删除的对象。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-02-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多