【问题标题】:Extract item in a list if Contains returns true如果 Contains 返回 true,则提取列表中的项目
【发布时间】:2011-02-11 09:33:38
【问题描述】:

我有两个列表 A 和 B,在我的程序开始时,它们都填充了来自数据库的信息(列表 A = 列表 B)。我的程序运行,列表 A 被使用和修改,列表 B 被单独留下。过了一会儿,我用数据库中的新信息重新加载列表 B,然后对照列表 A 进行检查。

foreach (CPlayer player in ListA)
      if (ListB.Contains(player))
            -----

首先,对象 player 是从一个类中创建的,它的主要标识符是 player.Name。 如果 Name 相同,但其他变量不同,.Contains 是否仍会返回 true?

Class CPlayer(
      public CPlayer (string name)
              _Name = name

在 ---- 我需要使用 ListB 中导致 .Contains 返回 true 的项目,我该怎么做?

【问题讨论】:

    标签: c# list contains


    【解决方案1】:

    List.Contains 的默认行为是它使用默认的相等比较器。如果您的项目是引用类型,这意味着它将使用身份比较,除非您的类通过Equals 提供另一个实现。

    如果您使用的是 .NET 3.5,那么您可以将第二行更改为此将执行您想要的操作:

    if (ListB.Any(x => x.Name == player.Name))
    

    对于 .NET 2.0,您可以为您的类实现 EqualsGetHashCode,但这可能会在您不希望两个播放器对象比较相等(如果它们具有相同名称但不同)的其他情况下产生不良行为其他领域。

    另一种方法是将 Jon Skeet's answer 改编为 .NET 2.0。创建一个Dictionary<string, object> 并用listB 中所有玩家的名字填充它。然后要测试某个名字的玩家是否在listB中,你可以使用dict.ContainsKey(name)

    【讨论】:

    • @Matt:现在为 .NET 2.0 添加了可能的解决方案。
    【解决方案2】:

    如果您希望 .Contains 方法仅匹配 CPlayer.Name,则在 CPlayer 类中实现这些方法:

    public override bool Equals(object obj)
    {
        if (!(obj is CPlayer)
            return false;
        return Name == (obj as CPlayer).Name;
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
    

    如果您希望 Name 比较不区分大小写,请改用此 Equals 方法:

    public override bool Equals(object obj)
    {
        if (!(obj is CPlayer)
            return false;
        return Name.Equals((obj as CPlayer).Name, StringComparison.OrdinalIgnoreCase);
    }
    

    如果您这样做,您的 .Contains 调用将按您的意愿工作。 其次,如果您想在列表中选择此项,请执行以下操作:

    var playerB = ListB[ListB.IndexOf(player)];
    

    它使用相同的 .Equals 和 .GetHashCode 方法。

    统一更新: 这可能是一个主观的陈述,但如果您的 .Equals 方法在进行字符串比较之前比较了 Int 哈希值,您也可以从中挤出一些性能。

    查看 .NET 源代码(Reflector FTW),我可以看到似乎只有 HastTable 类使用 GetHashCode 来提高其性能,而不是每次都使用 .Equals 来比较对象。在像这样的小类的情况下,相等比较器很简单,一个单一的字符串比较。如果你在比较所有属性,那么比较两个整数会快得多(尤其是如果它们被缓存:))

    List.Contains 和 List.IndexOf 不使用哈希码,使用 .Equals 方法,因此我建议检查里面的哈希码。它可能不会有什么明显的,但是当你渴望得到每一个毫秒的执行时(并不总是一件好事,bug 嘿!:P)这可能会对某人有所帮助。只是说... :)

    【讨论】:

    • 您能否提供一些证据或理由来证明“如果您的 .Equals 方法在进行字符串比较之前比较了 Int 哈希值,您也可以从中挤出一些性能......”?
    • 在帖子中添加了解释。这部分是主观的..但只是部分)
    【解决方案3】:

    听起来这是你需要完成的:

    对于列表 A 中的每个玩家,找到列表 B 中具有相同名称的每个玩家,并将两个玩家带入相同的范围内。

    这是一种在查询中连接两个列表的方法:

    var playerPairs =
        from playerA in ListA
        join playerB in ListB on playerA.Name equals playerB.Name
        select new { playerA, playerB };
    
    foreach(var playerPair in playerPairs)
    {
        Console.Write(playerPair.playerA.Name);
        Console.Write(" -> ");
        Console.WriteLine(playerPair.playerB.Name);
    }
    

    【讨论】:

      【解决方案4】:

      假设您正在使用System.Collections.Generic.List 类,如果CPlayer 类没有实现IEquatable<T>,它将使用CPlayer 类的EqualsGetHashCode 函数来检查List有一个成员等于Contains 的参数。假设实现对你来说没问题,你可以像

      CPlayer listBItem = ListB.First(p => p == player);

      ListB获取实例

      【讨论】:

        【解决方案5】:

        Mark 建议的替代方法是构建一组名称并使用它:

        HashSet<string> namesB = new HashSet<string>(ListB.Select(x => x.Name));
        foreach (CPlayer player in ListA)
        {
            if (namesB.Contains(player.Name))
            {
                ...
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-12-23
          • 1970-01-01
          • 1970-01-01
          • 2018-12-26
          • 2015-07-06
          • 2015-04-10
          • 2011-02-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多