C# 预处理集合C的最有效方法#

C# 预处理集合C的最有效方法#,c#,algorithm,C#,Algorithm,我有一个C#对象数组,它需要保留,以便我尝试从中筛选重复项(不是相同的对象引用,只是相同的属性值)。捕获物是重复的,必须去的是第一个,最老的需要留下 使用IEnumerable的当前算法(半伪代码重命名为everything) object[] filter(object[] array) { var set = new HashSet<Guid>(); var filtered = new List&l

我有一个C#对象数组,它需要保留,以便我尝试从中筛选重复项(不是相同的对象引用,只是相同的属性值)。捕获物是重复的,必须去的是第一个,最老的需要留下

使用IEnumerable的当前算法(半伪代码重命名为everything)

        object[] filter(object[] array)
        {
            var set = new HashSet<Guid>();
            var filtered = new List<object>();

            for (int i = array.Length; i-- > 0;)
            {
                var item = array[i];

                if (!set.Contains(item.ID))
                {
                    set.Add(item.ID);
                    filtered = new List<object>(filtered.Prepend(item));
                }
            }

            return filtered.ToArray();
        }
object[]过滤器(object[]数组)
{
var set=新的HashSet();
var filtered=新列表();
对于(int i=array.Length;i-->0;)
{
变量项=数组[i];
如果(!set.Contains(item.ID))
{
set.Add(item.ID);
filtered=新列表(filtered.Prepend(项目));
}
}
返回过滤后的.ToArray();
}
我知道它目前是O(n),但我正在寻找一个非常有效的方法来做到这一点。如果可能,只使用数组,这样我就不需要使用.ToArray()并再次迭代


我可以将筛选后的数组设置为大小为array.length的数组,并将其向后放置,即“filtered[array.length-I]=item”,但我不希望有空值。

推到堆栈可以被视为添加到列表的开头,从堆栈中弹出可以被视为从列表的开头删除一个项目

Stack.Push
是一个固定时间操作,只要堆栈有足够的容量,如所述,因此您可以使用堆栈代替:

// using object[] doesn't make sense here as it doesn't have an ID property,
// so I have taken the liberty to create my interface
IHasID[] Filter(IHasID[] array)
{
    var set = new HashSet<Guid>();
    // if not many elements are expected to be filtered, giving the stack a initial capacity might be better
    var filtered = new Stack<IHasID>(/*array.Length*/);

    for (int i = array.Length; i-- > 0;)
    {
        var item = array[i];

        if (set.Add(item.ID))
        {

            filtered.Push(item);
        }
    }

    // ToArray creates an array in the pop order, O(n)
    // https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.toarray?view=net-5.0#remarks
    return filtered.ToArray();
}

interface IHasID
{
    Guid ID { get; }
}
//此处使用对象[]没有意义,因为它没有ID属性,
//所以我冒昧地创建了我的界面
IHasID[]筛选器(IHasID[]数组)
{
var set=新的HashSet();
//如果预计要过滤的元素不多,则为堆栈提供初始容量可能更好
var filtered=新堆栈(/*array.Length*/);
对于(int i=array.Length;i-->0;)
{
变量项=数组[i];
如果(设置添加(项目ID))
{
过滤、推送(项目);
}
}
//ToArray以pop顺序O(n)创建数组
// https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.toarray?view=net-5.0#备注
返回过滤后的.ToArray();
}
接口IHasID
{
Guid ID{get;}
}

推送到堆栈可以被认为是添加到列表的开头,从堆栈中弹出可以被认为是从列表的开头删除项目

Stack.Push
是一个固定时间操作,只要堆栈有足够的容量,如所述,因此您可以使用堆栈代替:

// using object[] doesn't make sense here as it doesn't have an ID property,
// so I have taken the liberty to create my interface
IHasID[] Filter(IHasID[] array)
{
    var set = new HashSet<Guid>();
    // if not many elements are expected to be filtered, giving the stack a initial capacity might be better
    var filtered = new Stack<IHasID>(/*array.Length*/);

    for (int i = array.Length; i-- > 0;)
    {
        var item = array[i];

        if (set.Add(item.ID))
        {

            filtered.Push(item);
        }
    }

    // ToArray creates an array in the pop order, O(n)
    // https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.toarray?view=net-5.0#remarks
    return filtered.ToArray();
}

interface IHasID
{
    Guid ID { get; }
}
//此处使用对象[]没有意义,因为它没有ID属性,
//所以我冒昧地创建了我的界面
IHasID[]筛选器(IHasID[]数组)
{
var set=新的HashSet();
//如果预计要过滤的元素不多,则为堆栈提供初始容量可能更好
var filtered=新堆栈(/*array.Length*/);
对于(int i=array.Length;i-->0;)
{
变量项=数组[i];
如果(设置添加(项目ID))
{
过滤、推送(项目);
}
}
//ToArray以pop顺序O(n)创建数组
// https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.toarray?view=net-5.0#备注
返回过滤后的.ToArray();
}
接口IHasID
{
Guid ID{get;}
}

只需使用LINQ,它将是单O(n)CPU、O(n)RAM直通迭代器,无需进一步分配:

var result = input.Reverse().DistinctBy(x=> x.YourKey);
实施示例如下:

您也可以这样做,因为它所做的只是创建组迭代器:

var result = input.Reverse().GroupBy(x=> x.YourKey).Select(x=> x.First());

只需使用LINQ,它将是单O(n)CPU、O(n)RAM直通迭代器,无需进一步分配:

var result = input.Reverse().DistinctBy(x=> x.YourKey);
实施示例如下:

您也可以这样做,因为它所做的只是创建组迭代器:

var result = input.Reverse().GroupBy(x=> x.YourKey).Select(x=> x.First());

您应该只检查
set.Add(item.ID)
是否返回true,而不是首先检查
是否包含
:“返回布尔值:如果元素被添加到HashSet对象,则返回true;如果元素已存在,则返回false。”听起来您需要一个堆栈……鉴于您不知道输出将有多大,要么需要迭代两次,要么无法直接获取数组。选择一个。您应该只检查
set.Add(item.ID)
是否返回true,而不是首先检查
包含的内容。:“返回布尔值:如果元素被添加到HashSet对象,则返回true;如果元素已存在,则返回false。”听起来您需要一个堆栈……鉴于您不知道输出将有多大,要么需要迭代两次,要么无法直接获取数组。挑一个。(这不符合OP的“不
.ToArray()
如果可能”的要求,但我认为这是不可能的)@canton7 OP说“如果可能”,所以这不完全是一个“要求”:-)谢谢你,清洁工,我会勾选这个作为答案。正如我所想,我真的没有比O(n)*2更好的了。我想看看我是否可以用一个数组来实现这一点,但我看不到其他方法。你知道堆栈的最大容量是多少吗?@AlexMika请参阅的文档。可以在创建堆栈时指定容量。如果不这样做,则会使用一些默认的、未记录的容量。当该数组已满时,它将自动增加容量,方法是创建一个更大的数组并将其所有元素复制到该数组中。这与
列表
的工作原理类似(这不符合OP的要求“不
.ToArray()
,如果可能的话”,但我认为这是不可能的)@canton7 OP说“如果可能的话”,所以这不完全是一个“要求”:-)谢谢你的清洁工,我会勾选这个作为答案。正如我所想,我真的没有比O(n)*2更好的了。我想看看我是否可以用一个数组来实现这一点,但我看不到其他方法。你知道堆栈的最大容量是多少吗?@AlexMika请参阅的文档。可以在创建堆栈时指定容量。如果你