C#如何避免列表中的重复?

时间:2017-01-05 10:03:55

标签: c# list hashset

我可以用什么方法来避免列表中的重复?

一种方法是当我添加一个新项目时,首先检查该元素是否存在,但这使我使用更多代码并迭代所有列表以检查它是否存在。

另一种我可以使用hashset的方法,如果我尝试添加一个新项,它本身会检查项是否存在,如果没有,它将添加新项,如果存在,则不执行任何操作。

但是我知道hashset的效率较低,需要的资源比列表要多,所以我不知道是否使用hashset来避免重复,这样可以很好地利用hashset。

还有其他选择吗?

感谢。

4 个答案:

答案 0 :(得分:4)

您可以在一行代码中实现这一目标: -

List<long> longs = new List<long> { 1, 2, 3, 4, 3, 2, 5 };

List<long> unique = longs.Distinct().ToList();

unique仅包含1,2,3,4,5

答案 1 :(得分:1)

List是一个可能包含重复项的数据结构。重复元素通过其索引消除歧义。

  

一种方法是当我添加一个新项目时,首先检查该元素是否存在,但这使我使用更多代码并迭代所有列表以检查它是否存在。

这是可能的,但它容易出错并且很慢。每次要添加元素时,都需要遍历整个列表。您也可能忘记检查代码中的某个位置。

  

另一种我可以使用hashset的方法,如果我尝试添加一个新项,它本身会检查项是否存在,如果没有,它将添加新项,如果存在,则不执行任何操作。

这是首选方式。最好使用标准库来强制执行您想要的约束。

  

但是我知道hashset的效率较低,需要的资源比列表要多,所以我不知道是否使用hashset来避免重复,这样可以很好地利用hashset。

效率取决于你想要做什么;见https://stackoverflow.com/a/23949528/1256041

  

还有其他选择吗?

您可以使用ISet实现自己的List。这会使插入速度变慢(您需要迭代整个集合),但您将获得O(1)随机访问。

答案 2 :(得分:1)

哈希集是检查项目是否存在的最佳方法,因为它是O(1)。

因此,您可以在列表和hashset中插入项目 在插入新项目之前,检查它是否存在于hashset中。

答案 3 :(得分:0)

您无法避免List中的重复项。没办法 - 没有项目验证。

如果您不打扰项目顺序 - 请使用HashSet

如果你想保留物品的顺序(实际上有一点含糊不清 - 应该在第一次加法的索引处或在最后一次加法的索引处出现项目)。但是你想确保所有项都是唯一的,那么你应该编写自己的List类。即实现IList<T>接口的东西:

public class ListWithoutDuplicates<T> : IList<T>

你在这里有不同的选择。例如。你应该决定什么对你更重要 - 快速添加或内存消耗。因为对于快速添加和包含操作,您应该使用一些基于散列的数据结构。哪个是无序的。下面是使用HashSet的示例实现,用于存储内部列表中存储的所有项的哈希值。您将需要以下字段:

private readonly HashSet<int> hashes = new HashSet<int>();
private readonly List<T> items = new List<T>();
private static readonly Comparer<T> comparer = Comparer<T>.Default;

添加项目很简单(警告:此处不再进行空检查) - 使用项目哈希码快速O(1)检查是否已添加。使用相同的方法删除项目:

public void Add(T item)
{
    var hash = item.GetHashCode();
    if (hashes.Contains(hash))
        return;

    hashes.Add(hash);
    items.Add(item);
}

public bool Remove(T item)
{
    var hash = item.GetHashCode();
    if (!hashes.Contains(hash))
        return false;

    hashes.Remove(item.GetHashCode());
    return items.Remove(item);
}

一些基于索引的操作:

public int IndexOf(T item)
{
    var hash = item.GetHashCode();
    if (!hashes.Contains(hash))
        return -1;

    return items.IndexOf(item);
}

public void Insert(int index, T item)
{
    var itemAtIndex = items[index];
    if (comparer.Compare(item, itemAtIndex) == 0)
        return;

    var hash = item.GetHashCode();

    if (!hashes.Contains(hash))
    {
        hashes.Remove(itemAtIndex.GetHashCode());
        items[index] = item;
        hashes.Add(hash);
        return;
    }

    throw new ArgumentException("Cannot add duplicate item");
}

public void RemoveAt(int index)
{
    var item = items[index];
    hashes.Remove(item.GetHashCode());
    items.RemoveAt(index);
}

剩下的:

public T this[int index]
{
    get { return items[index]; }
    set { Insert(index, value); }
}

public int Count => items.Count;
public bool Contains(T item) => hashes.Contains(item.GetHashCode());
public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();

就是这样。现在你有了列表实现,它只会添加一次项目(第一次)。 E.g。

var list = new ListWithoutDuplicates<int> { 1, 2, 1, 3, 5, 2, 5, 3, 4 };

将创建包含项目1,2,3,5,4的列表。注意:如果内存消耗比性能更重要,那么使用items.Contains操作代替使用哈希值,即O(n)。

BTW我们刚才所做的实际上是一个IList Decorator