在C#的日常开发中,处理List集合时查找重复元素或者去除重复项是常见需求,传统的循环遍历对比方式代码繁琐且容易出错,使用LINQ的GroupBy与Where方法结合可以以极简的代码实现相关功能,下面介绍具体的实现方式。

查找List中的重复元素
GroupBy方法可以按照指定的键对集合元素进行分组,分组后每个组的Key就是分组的依据,组的元素数量就是该键对应的元素个数。结合Where方法筛选元素数量大于1的组,就能得到所有重复的元素。
首先定义一个简单的实体类用于测试:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
假设我们有一个User类型的List,需要查找Name重复的所有元素,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
// 初始化测试数据
List<User> userList = new List<User>
{
new User { Id = 1, Name = "张三", Age = 20 },
new User { Id = 2, Name = "李四", Age = 22 },
new User { Id = 3, Name = "张三", Age = 21 },
new User { Id = 4, Name = "王五", Age = 23 },
new User { Id = 5, Name = "李四", Age = 24 }
};
// 查找Name重复的元素
var duplicateUsers = userList
.GroupBy(u => u.Name) // 按照Name分组
.Where(g => g.Count() > 1) // 筛选分组后元素数量大于1的组
.SelectMany(g => g) // 将分组后的元素展开为扁平集合
.ToList();
// 输出结果
Console.WriteLine("重复的User元素:");
foreach (var user in duplicateUsers)
{
Console.WriteLine($"Id:{user.Id}, Name:{user.Name}, Age:{user.Age}");
}
}
}
上述代码执行后,会输出所有Name为张三和李四的User对象,因为这些Name在集合中存在重复。
结合GroupBy与Where实现去重
去重的逻辑和查找重复元素类似,只需要调整Where的筛选条件,或者选择分组的第一个元素即可。如果需求是保留重复项中的第一个元素,实现代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<User> userList = new List<User>
{
new User { Id = 1, Name = "张三", Age = 20 },
new User { Id = 2, Name = "李四", Age = 22 },
new User { Id = 3, Name = "张三", Age = 21 },
new User { Id = 4, Name = "王五", Age = 23 },
new User { Id = 5, Name = "李四", Age = 24 }
};
// 按照Name去重,保留每个Name的第一个元素
var distinctUsers = userList
.GroupBy(u => u.Name)
.Select(g => g.First()) // 取每个分组的第一个元素
.ToList();
Console.WriteLine("去重后的User元素:");
foreach (var user in distinctUsers)
{
Console.WriteLine($"Id:{user.Id}, Name:{user.Name}, Age:{user.Age}");
}
}
}
如果需要去除所有重复的元素,只保留完全没有重复的项,只需要筛选分组数量为1的组即可:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<User> userList = new List<User>
{
new User { Id = 1, Name = "张三", Age = 20 },
new User { Id = 2, Name = "李四", Age = 22 },
new User { Id = 3, Name = "张三", Age = 21 },
new User { Id = 4, Name = "王五", Age = 23 },
new User { Id = 5, Name = "李四", Age = 24 }
};
// 去除所有重复元素,只保留唯一项
var uniqueUsers = userList
.GroupBy(u => u.Name)
.Where(g => g.Count() == 1) // 筛选分组数量为1的组
.Select(g => g.First())
.ToList();
Console.WriteLine("仅保留唯一项的User元素:");
foreach (var user in uniqueUsers)
{
Console.WriteLine($"Id:{user.Id}, Name:{user.Name}, Age:{user.Age}");
}
}
}
多条件分组去重
如果需要按照多个属性进行分组去重,只需要修改GroupBy的键选择器即可,比如同时按照Name和Age分组:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<User> userList = new List<User>
{
new User { Id = 1, Name = "张三", Age = 20 },
new User { Id = 2, Name = "张三", Age = 20 },
new User { Id = 3, Name = "张三", Age = 21 },
new User { Id = 4, Name = "李四", Age = 22 }
};
// 按照Name和Age两个条件分组去重
var multiDistinctUsers = userList
.GroupBy(u => new { u.Name, u.Age }) // 使用匿名对象作为分组键
.Select(g => g.First())
.ToList();
Console.WriteLine("多条件去重后的User元素:");
foreach (var user in multiDistinctUsers)
{
Console.WriteLine($"Id:{user.Id}, Name:{user.Name}, Age:{user.Age}");
}
}
}
这种方式不需要手动编写循环对比逻辑,代码简洁且可读性强,同时LINQ的底层优化也能保证较好的执行效率,适合大多数List重复元素处理的场景。
注意事项
- GroupBy分组的键需要实现正确的
Equals和GetHashCode方法,如果是自定义引用类型作为分组键,需要确保类型正确重写了这两个方法,否则可能导致分组不符合预期。 - 如果集合数据量非常大,GroupBy会一次性加载所有分组到内存中,此时需要考虑内存占用问题,必要时可以分批次处理数据。
- SelectMany方法用于将多个集合展开为一个集合,在查找重复元素时如果需要得到所有重复的元素,必须要使用这个方法,否则得到的是分组对象而不是具体的元素。