File: Extensions\IListExtensions.cs
Web Access
Project: src\src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.Package.csproj (Microsoft.CodeAnalysis.Collections.Package)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Microsoft.CodeAnalysis;
 
internal static class IListExtensions
{
    public static bool HasDuplicates<T>(this IReadOnlyList<T> list)
        => list.HasDuplicates(EqualityComparer<T>.Default);
 
    public static bool HasDuplicates<T>(this IReadOnlyList<T> list, IEqualityComparer<T> comparer)
        => list.HasDuplicates(static x => x, comparer);
 
    public static bool HasDuplicates<TItem, TValue>(this IReadOnlyList<TItem> list, Func<TItem, TValue> selector)
        => list.HasDuplicates(selector, EqualityComparer<TValue>.Default);
 
    /// <summary>
    /// Determines whether duplicates exist using given equality comparer.
    /// </summary>
    /// <param name="list">Array to search for duplicates</param>
    /// <returns>Whether duplicates were found</returns>
    /// <remarks>
    /// API proposal: https://github.com/dotnet/runtime/issues/30582.
    /// <seealso cref="ImmutableArrayExtensions.HasDuplicates{TItem, TValue}(System.Collections.Immutable.ImmutableArray{TItem}, Func{TItem, TValue}, IEqualityComparer{TValue})"/>
    /// <seealso cref="Roslyn.Utilities.EnumerableExtensions.HasDuplicates{TItem, TValue}(IEnumerable{TItem}, Func{TItem, TValue}, IEqualityComparer{TValue})"/>
    /// </remarks>
    internal static bool HasDuplicates<TItem, TValue>(this IReadOnlyList<TItem> list, Func<TItem, TValue> selector, IEqualityComparer<TValue> comparer)
    {
        switch (list.Count)
        {
            case 0:
            case 1:
                return false;
 
            case 2:
                return comparer.Equals(selector(list[0]), selector(list[1]));
 
            default:
                var set = comparer == EqualityComparer<TValue>.Default ? PooledHashSet<TValue>.GetInstance() : new HashSet<TValue>(comparer);
                var result = false;
 
                // index to avoid allocating enumerator
                for (int i = 0, n = list.Count; i < n; i++)
                {
                    if (!set.Add(selector(list[i])))
                    {
                        result = true;
                        break;
                    }
                }
 
                (set as PooledHashSet<TValue>)?.Free();
                return result;
        }
    }
}