检查集合中是否已存在对象

时间:2010-12-17 21:42:01

标签: c# list equality

我正在学习编程,我的问题是我有一堆对象,我只想在列表尚未包含该对象的情况下将这些对象添加到列表中。其次,如果对象已经包含,我想忽略该对象并添加下一个对象。我想我的第一部分工作只需要第二部分的帮助。非常感谢。

PartyGroup partyGroup = new PartyGroup(); 

using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {  
        if (!myPartyGroupList.Contains(partyGroup)) 
        { 
            partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
            partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
            partyGroup.PersonList = myPersonList; 

            myPartyGroupList.Add(partyGroup); 
        } 
        else 
        { 
            //?? 
        } 
    } 
} 

6 个答案:

答案 0 :(得分:5)

你的第一部分完美无缺。

只需删除'else'子句,您的例程将自动在下一次迭代中添加下一个元素。像这样:

while (reader.Read()) 
{ 
    if (!myPartyGroupList.Contains(partyGroup)) 
    { 
        partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
        partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
        partyGroup.PersonList = myPersonList; 

        myPartyGroupList.Add(partyGroup); 

    } 
} 

答案 1 :(得分:4)

您的代码存在一些问题。首先,您为每个迭代重用相同的对象。

考虑

List<Foo> foos = new List<Foo>();
Foo foo = new Foo();
foo.Bar = "Alpha";
foos.Add(foo);
foo.Bar = "Beta";
foos.Add(foo);

您会注意到您的列表将包含2个项目,但它们将引用同一个对象。如果您遍历列表并检查Bar,则每个都会返回"Beta"

您想为每件商品创建 Foo

List<Foo> foos = new List<Foo>();
Foo foo = new Foo();
foo.Bar = "Alpha";
foos.Add(foo);
Foo anotherFoo = new Foo();
anotherFoo.Bar = "Beta";
foos.Add(anotherFoo);

在循环术语中,这基本上只意味着在循环内而不是外部创建对象。

while (someCondition)
{
    Foo foo = new Foo();
    // do your work, populate the object, etc.
    // then check contains 
    if (!myList.Contains(foo))
        myList.Add(foo);
}

关于检查集合是否已包含该对象,您是否正确覆盖了EqualsGetHashCode?处理类时,默认行为是简单地检查对象引用是否相等。如果您担心对象是封装的,那么您需要自己提供逻辑。在您的课程中,您需要覆盖EqualsGetHashCode方法,以实现所需的确定相等的方法。

class Foo
{
    public string Bar { get; set; }

    public override int GetHashCode()
    {
        return this.Bar.GetHashCode();
    }

    public override bool Equals(object other)
    {
        Foo otherFoo = other as Foo;
        if (otherFoo == null)
            return false;
        else
            return this.Bar == otherFoo.Bar;
    }
}

现在当Contains试图确定对象是否已经在列表中时,它将根据对象中包含的值而不是内存引用来执行此操作。

答案 2 :(得分:3)

比较时,最好使用与标识符的比较,在您的情况下是PartyGroupId。如果使用contains然后是Contains()的默认重载,那么列表中对象的哈希值将用于比较。

因此,不是将比较保留给.NET,您可以创建自定义IEqualityComparer实现或使用Linq的Where子句,如下所示。


using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {  
        int groupId = Convert.ToInt32(reader["party_group_id"]);

        if (partyGroupsList.Where(partyGroup => partyGroup.PartyGroupID == groupId).Any() == false)
        {
           PartyGroup newPartyGroup = new PartyGroup()
                                      {
                                          PartyGroupID = groupId,
                                          PartyGroupName = reader["party_group_name"].ToString(),
                                          PersonList = myPersonList
                                      };

           partyGroupsList.Add(newPartyGroup);                 
        } 

        // If object already exists in the list then do not add, continue 
        // to the next row.   
    } 
} 

另一个建议是将PartyGroup类成员重命名为:


class PartyGroup
 {
   public int ID { get; set; }
   public string Name { get; set; }
   public IList PersonList { get; set; }
}

答案 3 :(得分:1)

您可以考虑使用Hashset<PartyGroup>填充,然后将其转换为列表。如果你有大量的项目,那将比检查每个项目的列表快得多。

Hashset<PartyGroup> pgHash = new Hashset<PartyGroup>();

using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {
        PartyGroup pg = new PartyGroup();  
        partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
        partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
        partyGroup.PersonList = myPersonList; 
        // Add won't add an item if it already exists in the collection.
        pgHash.Add(partyGroup); 
    } 
}
// Now convert the result to a list.
myPartyGroupList = pgHash.ToList();

如果您的PartyGroup类未实现IEquatable<PartyGroup>,则必须提供相等比较器。以下应该有效:

public class PartyGroupComparer:IEqualityComparer<PartyGroup>
{
    public bool Equals(PartyGroup g1, PartyGroup g2)
    {
        return g1.PartyGroupId.Equals(g2.PartyGroupId);
    }

    public int GetHashCode(PartyGroup g)
    {
        return g.PartyGroupId;
    }
}

然后你的初始化变为:

IEqualityComparer<PartyGroup> pgComparer = new PartyGroupComparer();
HashSet<PartyGroup> pgHash = new HashSet<PartyGroup>(pgComparer);

正如其他人所指出的,HashSet的替代方案是Dictionary。这将阻止您必须进行相等比较。完成后,您仍然需要转换为列表。但这很简单:

Dictionary<int, PartyGroup> dict = new Dictionary<int, PartyGroup>();
// populate dictionary as suggested in other answer

// now convert values to a list.
myPartyGroupList = dict.Values.ToList();

答案 4 :(得分:0)

试试这个

while (reader.Read()) 
{ 
    partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
    partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
    partyGroup.PersonList = myPersonList; 

    if (!myPartyGroupList.Contains(partyGroup)) 
    { 
        myPartyGroupList.Add(partyGroup); 
    } 
} 

答案 5 :(得分:0)

首先,您要检查对象实例是否在集合中,但您只创建一个实例(在while循环之外)。因此,当您检查!myPartyGroupList.Contains(partyGroup)时,它将在第一次返回false,因此您将obj添加到集合中,然后每次都返回false。

我会使用一个使用Id属性的Dictionary作为Dictionary键。

像这样:

Dictionary <int,PartyGroup> myPartyGroupList = new Dictionary <int,PartyGroup>();

using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {  
        int id=Convert.ToInt32(reader["party_group_id"]); 
        if (!myPartyGroupList.ContainsKey( id )) 
        { 
            PartyGroup partyGroup = new PartyGroup(); 

            partyGroup.PartyGroupID = id; 
            partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
            partyGroup.PersonList = myPersonList; 

            myPartyGroupList.Add(id, partyGroup);  // key, value? check param order here
        } 
    } 
}