C# 将对象的最后一次出现添加到按最近添加顺序排列的列表中

C# 将对象的最后一次出现添加到按最近添加顺序排列的列表中,c#,performance,logging,C#,Performance,Logging,我需要一个各种各样的日志,必须不时地转储内容。 我只想获取每个项目的最后一个实例,并保留该顺序 有没有比这样更好的方法?contains非常昂贵,但我对额外哈希集的内存开销也不满意 public List<Int3> UnsafeDumpMostRecentUsageLast() { HashSet<Int3> _containsHelper = new HashSet<Int3>(); List<Int3> u

我需要一个各种各样的日志,必须不时地转储内容。 我只想获取每个项目的最后一个实例,并保留该顺序

有没有比这样更好的方法?contains非常昂贵,但我对额外哈希集的内存开销也不满意

 public List<Int3> UnsafeDumpMostRecentUsageLast() {

        HashSet<Int3> _containsHelper = new HashSet<Int3>();
        List<Int3> uniqueOccurencesOrdered = new List<Int3>(uniqueConsumedCount);

        for (int i = usageLog.Length-1;i >= 0; i--) {

            if (_containsHelper.Add(usageLog[i])) 
                uniqueOccurencesOrdered.Add(usageLog[i]);

        }
        uniqueOccurencesOrdered.Reverse();
        return uniqueOccurencesOrdered;
    }
public List UnsafeDumpMostRecentUsageLast(){
HashSet _containsHelper=新HashSet();
List UniqueOccurrenceSordered=新列表(uniqueConsumedCount);
对于(int i=usageLog.Length-1;i>=0;i--){
如果(_containsHelper.Add(usageLog[i]))
唯一发生排序添加(usageLog[i]);
}
UniqueOccurrenceSordered.Reverse();
返回唯一发生排序;
}
把它弄清楚。。假设我有这样一个列表:

var str = "aabbbaabbbccabccccdeddaccc";
var result = new string(str
    .Reverse()
    .Distinct()
    .Reverse()
    .ToArray()
);
aabbbabbbccabcccdedcc

我想返回一个返回大写字母的列表:

aabbbabbbccabcccdedcc

所以名单应该是:贝达克

不是: aabbbabbbccabcccdedcc


ABCDE

我不确定
Int3
类型是什么,但是如果我们假设我们谈论的是一个int(在其他情况下工作类似),并且
usageLog
是一个
IEnumerable
您可以执行以下操作:

public List<Int3> UnsafeDumpMostRecentUsageLast() {          
    return usageLog.Distinct().ToList();
}
public List UnsafeDumpMostRecentUsageLast(){
返回usageLog.Distinct().ToList();
}
添加

如果您的列表已经排序,并且您只想保留每个实例的最后一次出现(如您的注释所示):


usageLog.Reverse().Distinct().Reverse().ToList()

我不确定
Int3
类型是什么,但是如果我们假设我们谈论的是一个int(在其他情况下工作类似),并且
usageLog
是一个
IEnumerable
,那么您可以执行以下操作:

public List<Int3> UnsafeDumpMostRecentUsageLast() {          
    return usageLog.Distinct().ToList();
}
public List UnsafeDumpMostRecentUsageLast(){
返回usageLog.Distinct().ToList();
}
添加

如果您的列表已经排序,并且您只想保留每个实例的最后一次出现(如您的注释所示):


usageLog.Reverse().Distinct().Reverse().ToList()

您可以执行以下操作:

var str = "aabbbaabbbccabccccdeddaccc";
var result = new string(str
    .Reverse()
    .Distinct()
    .Reverse()
    .ToArray()
);

您可以这样做:

var str = "aabbbaabbbccabccccdeddaccc";
var result = new string(str
    .Reverse()
    .Distinct()
    .Reverse()
    .ToArray()
);

基于哈希的查找为您提供了最佳的时间复杂度(从而提高了性能)。如果您对
HashSet
类空间开销(以及扩展内部存储和重新灰化的额外成本)不满意,那么为所需的操作创建自己的哈希结构并不困难

例如,下面的算法使用大小为
N
的2
int
数组以更少的开销实现相同的目标。第一个名为
head
的数组用于哈希表bucket链表开始索引,而
next
保存bucket中下一个条目的索引,还用于标识最后一个唯一条目。不需要存储这些值,因为我们已经有了它们,而且整个映射都是按索引进行的

static List<T> UnsafeDumpMostRecentUsageLast<T>(IReadOnlyList<T> source, IEqualityComparer<T> comparer = null)
{
    var head = new int[source.Count];
    var next = new int[source.Count];
    int count = 0;
    if (comparer == null) comparer = EqualityComparer<T>.Default;
    for (int i = 0; i < source.Count; i++)
    {
        var item = source[i];
        // Check for duplicate
        int bucket = (comparer.GetHashCode(item) & int.MaxValue) % head.Length;
        int prev = -1, last = head[bucket] - 1;
        while (last >= 0 && !comparer.Equals(source[last], item))
            last = next[prev = last];
        if (last >= 0)
        {
            // Found, replace it in the hash chain (we need only the last) 
            next[i] = next[last];
            if (prev >= 0)
                next[prev] = i;
            else
                head[bucket] = i + 1;
            // Use int.MinValue (value < -1) to mark the duplicate entry as not being last
            next[last] = int.MinValue;
        }
        else
        {
            next[i] = head[bucket];
            head[bucket] = i + 1;
            count++;
        }
    }
    var result = new List<T>(count);
    for (int i = 0; i < next.Length; i++)
    {
        if (next[i] < -1) continue;
        result.Add(source[i]);
        if (result.Count == count) break;
    }
    return result;
}

基于哈希的查找为您提供了最佳的时间复杂度(从而提高了性能)。如果您对
HashSet
类空间开销(以及扩展内部存储和重新灰化的额外成本)不满意,那么为所需的操作创建自己的哈希结构并不困难

例如,下面的算法使用大小为
N
的2
int
数组以更少的开销实现相同的目标。第一个名为
head
的数组用于哈希表bucket链表开始索引,而
next
保存bucket中下一个条目的索引,还用于标识最后一个唯一条目。不需要存储这些值,因为我们已经有了它们,而且整个映射都是按索引进行的

static List<T> UnsafeDumpMostRecentUsageLast<T>(IReadOnlyList<T> source, IEqualityComparer<T> comparer = null)
{
    var head = new int[source.Count];
    var next = new int[source.Count];
    int count = 0;
    if (comparer == null) comparer = EqualityComparer<T>.Default;
    for (int i = 0; i < source.Count; i++)
    {
        var item = source[i];
        // Check for duplicate
        int bucket = (comparer.GetHashCode(item) & int.MaxValue) % head.Length;
        int prev = -1, last = head[bucket] - 1;
        while (last >= 0 && !comparer.Equals(source[last], item))
            last = next[prev = last];
        if (last >= 0)
        {
            // Found, replace it in the hash chain (we need only the last) 
            next[i] = next[last];
            if (prev >= 0)
                next[prev] = i;
            else
                head[bucket] = i + 1;
            // Use int.MinValue (value < -1) to mark the duplicate entry as not being last
            next[last] = int.MinValue;
        }
        else
        {
            next[i] = head[bucket];
            head[bucket] = i + 1;
            count++;
        }
    }
    var result = new List<T>(count);
    for (int i = 0; i < next.Length; i++)
    {
        if (next[i] < -1) continue;
        result.Add(source[i]);
        if (result.Count == count) break;
    }
    return result;
}

那会给我错误的顺序。我用一个例子更新了我的OP。。。将“返回usageLog.Reverse().Distinct().Reverse().ToList();”做我想做的吗?@user3488765更新了answerWow,这被认为比原始实现性能更好-为
Distiinct
+设置了额外的缓冲区,用于
反向
+来自
ToList
的额外列表。真可笑。最好删除
performance
标记。@IvanStoev我删除了接受答案。在Rob发布了与SteelSoul相同的帖子,我看到了他的名声之后,我没有对答案做更多的研究,因为我认为如果他们费心留下回复,他们会知道得更好。你知道比我原来的方法更好的方法吗?事实上没有:)我认为这是最好的。我看到的唯一潜在的改进是用一些定制的轻量级等价物替换
哈希集
,主要是为了避免重新分配,因为没有办法预先指定哈希集的容量。这会给我错误的顺序。我用一个例子更新了我的OP。。。将“返回usageLog.Reverse().Distinct().Reverse().ToList();”做我想做的吗?@user3488765更新了answerWow,这被认为比原始实现性能更好-为
Distiinct
+设置了额外的缓冲区,用于
反向
+来自
ToList
的额外列表。真可笑。最好删除
performance
标记。@IvanStoev我删除了接受答案。在Rob发布了与SteelSoul相同的帖子,我看到了他的名声之后,我没有对答案做更多的研究,因为我认为如果他们费心留下回复,他们会知道得更好。你知道比我原来的方法更好的方法吗?事实上没有:)我认为这是最好的。我所看到的唯一潜在改进是用一些定制的轻量级等价物替换
哈希集
,主要是为了避免重新分配,因为没有办法预先指定哈希集的容量。List.contains太贵了,但我对额外哈希集的内存开销也不满意。然后你可以考虑,例如从