使用实现IComparable的元素的SortedSet不会正确删除元素

时间:2015-04-09 10:01:14

标签: c# icomparable sortedset

我有一个带有我的数据结构的排序集,其中包含一个id(字符串)和一个日期。我想避免使用id重复,并使用日期将元素排序在集合中,所以我使我的数据结构以这种方式实现IComparable<T>接口:

public class PreLobbyPlayer : IComparable<PreLobbyPlayer>
{
    public int CompareTo(PreLobbyPlayer other)
    {
        // First avoid duplicate players
        int compResult = this.SPlayerId.CompareTo( other.SPlayerId);
        if ( compResult == 0 )
            return compResult;

        // Then compare join dates
        compResult = this.DJoinDate.CompareTo(other.DJoinDate);
        // If dates are equal, get the first, but we don't 
        // prevent insertion of two different players with the same date
        return compResult == 0 ? -1 : compResult;
    }
}

然后我尝试使用带有LINQ方法RemoveWhere(p => p.SPlayerId == sPlayerId)的id删除一个元素,其中sPlayerId是代码中其他地方指定的id。

事实是有时候元素没有删除,RemoveWhere(...)返回0而元素仍然在SortedSet<PreLobbyPlayer>

我编写了一些调试信息,这就是结果:

jor0|jor11|jor12|jor8|jor5|jor14|jor9|jor3|jor13|jor15|jor10|jor7|jor4|jor1|jor2|

Leave for user jor6, playerCount is: 15, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor11|jor12|jor8|jor5|jor14|jor9|jor13|jor3|jor10|jor15|jor4|jor7|jor1|jor2|

Leave for user jor7, playerCount is: 15, removed is 0, prevSOwnerPlayerId is jor0

jor0|jor11|jor12|jor8|jor14|jor9|jor13|jor3|jor10|jor15|jor4|jor7|jor1|jor2|

Leave for user jor5, playerCount is: 14, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor11|jor12|jor8|jor14|jor9|jor3|jor13|jor15|jor10|jor7|jor1|jor2|

Leave for user jor4, playerCount is: 13, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor12|jor8|jor14|jor9|jor13|jor3|jor10|jor15|jor7|jor1|jor2|

Leave for user jor11, playerCount is: 12, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor12|jor8|jor14|jor9|jor3|jor13|jor15|jor10|jor7|jor1|

Leave for user jor2, playerCount is: 11, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor12|jor8|jor14|jor9|jor13|jor3|jor10|jor15|jor7|jor1|

Leave for user jor15, playerCount is: 11, removed is 0, prevSOwnerPlayerId is jor0

jor0|jor8|jor14|jor9|jor13|jor3|jor10|jor15|jor7|jor1|

Leave for user jor12, playerCount is: 10, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor8|jor14|jor9|jor3|jor13|jor15|jor10|jor7|

Leave for user jor1, playerCount is: 9, removed is 1, prevSOwnerPlayerId is jor0

jor0|jor8|jor9|jor13|jor3|jor10|jor15|jor7|

Leave for user jor14, playerCount is: 8, removed is 1, prevSOwnerPlayerId is jor0

jor8|jor9|jor3|jor13|jor15|jor10|jor7|

Leave for user jor0, playerCount is: 7, removed is 1, prevSOwnerPlayerId is jor0

jor8|jor13|jor3|jor10|jor15|jor7|

Leave for user jor9, playerCount is: 6, removed is 1, prevSOwnerPlayerId is jor8

jor8|jor3|jor13|jor15|jor10|jor7|

Leave for user jor10, playerCount is: 6, removed is 0, prevSOwnerPlayerId is jor8

jor8|jor13|jor15|jor10|jor7|

Leave for user jor3, playerCount is: 5, removed is 1, prevSOwnerPlayerId is jor8

jor8|jor10|jor15|jor7|

Leave for user jor13, playerCount is: 4, removed is 1, prevSOwnerPlayerId is jor8

jor15|jor10|jor7|

Leave for user jor8, playerCount is: 3, removed is 1, prevSOwnerPlayerId is jor8

&#34; jorxx&#34;的列表ids是在调用RemoveWhere之后打印SortedSet元素的所有ID。 remove是RemoveWhere返回的值,playerCount是调用RemoveWhere后SortedSet的长度,&#34; Leave for user&#34;指定删除的元素的id,您可以忽略其余的。

正如您在本案中所看到的那样,尽管它们存在于SortedSet中,但不会删除带有id jor15,jor7和jor10的元素。每次我尝试时,插入顺序和日期都不同,因此其他元素都会失败。甚至有时会成功删除所有元素。我想如果我将使用相同的插入顺序和日期,结果将是相同的但我必须更改代码太多以测试它。是的,我懒惰;)

我让它将CompareTo函数更改为:

return this.SPlayerId.CompareTo(other.SPlayerId);

并在需要时使用OrderBy由DJoinDate排序,但我想知道为什么我之前的CompareTo实现会破坏SortedSet逻辑。

编辑:

正如PetSerAl指出的那样,当日期被视为等于时,我的CompareTo方法没有给出一致的结果。

我将CompareTo更改为:

public class PreLobbyPlayer : IComparable<PreLobbyPlayer>
{
    public int CompareTo(PreLobbyPlayer other)
    {
        // First avoid duplicate players
        int compIdResult = this.SPlayerId.CompareTo( other.SPlayerId);
        if ( compIdResult == 0 )
            return compIdResult;

        // Then compare join dates
        int compDateResult = this.DJoinDate.CompareTo(other.DJoinDate);
        // If dates are equal, return the id comparison result to give consistent results.
        return compDateResult == 0 ? compIdResult : compDateResult;
    }
}

并且像魅力一样工作。感谢。

编辑:

正如@PetSerAl再次指出:),我的PreLobbyPlayer类的CompareTo方法的第二个版本仍然给出不一致的结果。您可以按照接受的答案及其评论中的说明进行操作。基本上,您可能会使用包含具有相同ID的PreLobbyPlayers的SortedSet结束,这对我不利。 SortedSet使用相同的排序逻辑来避免重复,并且可以省略元素之间的一些比较(我不抱怨SortedSet实现,它是正常和有效的)。我无法为这个案例找出一致的CompareTo(PreLobbyPlayer其他)实现,欢迎提出想法和建议。

我的最终解决方案是仅使用id来避免重复,并在需要时使用日期和LINQ的OrderBy方法对集合进行排序。对我来说,这是可以接受的,因为SortedSet将包含不超过100个元素,并且当我需要按日期排序的集合时,逻辑中只有一个案例。

public class PreLobbyPlayer : IComparable<PreLobbyPlayer>
{
   public int CompareTo(PreLobbyPlayer other)
   {
       return this.SPlayerId.CompareTo( other.SPlayerId);
   }
   ...
}

2 个答案:

答案 0 :(得分:0)

  

如果日期相同,请先获得,但我们不会   防止插入两个具有相同日期的不同玩家

什么是“第一”?请考虑以下两个示例:a.CompareTo(b)b.CompareTo(a)。它们是否提供一致的结果?

您的CompareTo还有其他不一致:a={Id1,DateA}b={Id2,DateB}c={Id1,DateC}其中DateA<DateB<DateC。使用您的代码,您将拥有:a<bb<ca=c。因此,您的CompareTo实施并不总是阻止向SPlayerId添加具有相同SortedSet的两个元素。

var a=new PreLobbyPlayer { SPlayerId=1,DJoinDate=DateTime.Today };
var b=new PreLobbyPlayer { SPlayerId=2,DJoinDate=DateTime.Today.AddDays(1) };
var c=new PreLobbyPlayer { SPlayerId=1,DJoinDate=DateTime.Today.AddDays(2) };
var set=new SortedSet<PreLobbyPlayer>();
set.Add(b);
set.Add(a);
set.Add(c);
foreach(var current in set) {
    Console.WriteLine("{0}: {1}",current.SPlayerId,current.DJoinDate);
}

答案 1 :(得分:0)

如果正在比较2 PreLobbyPlayer个具有相同DJoinDate(但不同的id&#39; s)的对象a和b,则比较结果是随机的。
取决于哪一个&#39;这个&#39;哪一个是&#39;其他&#39;有时a会比b大,有时b会比a大。

如果DJoinDate相同,您可以只比较ID,这样订单总是一样的。

public int CompareTo(PreLobbyPlayer other)
{
    // First avoid duplicate players
    int idCompare = this.SPlayerId.CompareTo(other.SPlayerId);
    if (idCompare == 0) return 0;

    // Then compare join dates
    int dateCompare = this.DJoinDate.CompareTo(other.DJoinDate);
    // If dates are equal, get the result from the first comparison 
    return dateCompare == 0 ? idCompare : dateCompare;
}