获取字典中的最后一个元素?

31

我的字典:

Dictionary<double, string> dic = new Dictionary<double, string>();

如何返回字典中的最后一个元素?


定义最后一个元素:最后插入的?最后排序的元素? - Sasha
2
有趣的是,有多少个赞成不正确的答案! - RichardOD
次级的,看看我的帖子 - 我提到了OrderedDictionary集合类 - 这可能是你最好的选择。普通字典不保留插入顺序信息。 - LBushkin
1
@LBushkin- 这就是为什么我建议创建一个包装类。 - RichardOD
RichardOD的包装类看起来非常完美。 - Metro Smurf
12个回答

69

“Last”指什么?是指最后添加的值吗?

Dictionary<TKey,TValue>类是一个无序集合。添加或删除项可能会改变被视为第一个和最后一个元素的内容。因此,没有办法获取最后添加的元素。

可以使用有序字典类SortedDictionary<TKey,TValue>。但这将基于键值的比较而不是按值添加的顺序进行排序。

编辑

一些人提到了使用以下LINQ风格的方法:

var last = dictionary.Values.Last();

使用这种方法要非常谨慎。它将返回Values集合中的最后一个值,这可能是您添加到字典中的最后一个值,也可能不是。它可能是也可能不是。


5
谢谢警告不要使用LINQ风格的方法,因为它无法返回subprime所需的内容。请点赞表示支持。 - RichardOD
6
尽管没有通用版本,但在System.Collection.Specialized命名空间中有一个OrderedDictionary可维护插入顺序的项。 - LBushkin
1
在这种情况下,使用LastOrDefault而不是Last。 - JaredPar

25

字典是无序集合,因此不存在第一个或最后一个元素的概念。如果你想要一个类似于字典但能维护插入顺序的集合,可以考虑使用OrderedDictionary

如果你想要一个能对元素进行排序的集合,可以考虑使用SortedDictionary<TKey,TValue>

如果你已经有了一个字典,并且你想要根据某个排序顺序找到“最后”的元素,你可以使用 LINQ 对集合进行排序,类似于:

myDictionary.Values.OrderBy( x => x.Key ).Last();

谨慎使用Dictionary.Keys.Last() - 尽管键列表是使用键类型的默认IComparer进行排序,但您得到的值可能不是您期望的值。


11

我知道这个问题太老了,不可能获得任何赞,但我不喜欢任何一个答案,所以我会发表自己的答案,希望为未来的读者提供另一种选择。

假设你想要字典中的最高键值,而不是最后插入的:

以下内容在.NET 4.0上对我没有用:

myDictionary.Values.OrderBy( x => x.Key ).Last();
我怀疑问题在于 'x' 代表字典中的一个值,而值没有键(字典存储键,不存储字典值)。我也可能在使用技术时犯了错误。
无论如何,对于大型字典来说,这种解决方法会很慢,对于计算机科学专业的人来说,这可能是 O(n log n) 的,因为它正在排序整个字典以获取一个条目。这就像重新排列整个 DVD 集合以找到一个特定的电影。
var lastDicVal = dic.Values.Last();

已经被广泛认为是一个不好的做法。在实践中,该解决方案可能会返回字典中添加的最后一个值(而不是最高键值),但从软件工程的角度来看,这是毫无意义的,不应依赖于它。即使它在未来永远都能正常工作,它仍然代表着代码中的定时炸弹,依赖于库实现的细节。


我的解决方案如下:

var lastValue = dic[dic.Keys.Max()];

使用Keys.max()函数比排序要快得多,时间复杂度是O(n)而不是O(n log n)。 如果性能足够重要,甚至O(n)也太慢的话,可以在单独的变量中跟踪最后插入的键,用它来替换dic.Keys.Max(),这将使整个查找过程尽可能地快,即O(1)。

注意:将double或float作为键不是最佳实践,可能会产生出乎意料的结果,这超出了本文章的范围。请阅读有关浮点/双精度值上下文中“epsilon”的文章。


5
如果您正在使用.NET 3.5,请查看以下内容:
 dic.Keys.Last()

如果您想要一个可预测的顺序,请使用以下代码:
IDictionary<int, string> dic = new SortedDictionary<int, string>();

每次调用“Last()”都会返回相同的结果,直到进行插入或“Add()”操作,是吗?此时它可能会有所不同? - DevinB
我认为是这样,但由于它是一个无序集合,最后添加的可能在集合的中间某个位置。我不确定它在内部是如何实现的,或者Last()实际返回什么。需要有更多了解内部机制的人来回答这个问题。释放Skeet信号。 - Chris Doggett

3

使用以下方法代替:

Dictionary<double, string>

您可以使用以下方式:

List<KeyValuePair<double, string>>

这将允许您使用索引器按顺序而不是按键访问元素。

3
考虑创建一个自定义集合,其中在自定义集合的Add方法中包含一个引用。这将设置一个私有字段,其中包含最后添加的键/值(或两者都有),具体取决于您的要求。
然后再创建一个Last()方法来返回此内容。以下是一个概念证明类,以展示我的意思(请勿抱怨缺少接口实现等-这只是示例代码):
public class LastDictionary<TKey, TValue>
{
    private Dictionary<TKey, TValue> dict;

    public LastDictionary()
    {
        dict = new Dictionary<TKey, TValue>();
    }

    public void Add(TKey key, TValue value)
    {
        LastKey = key;
        LastValue = value;
        dict.Add(key, value);
    }

    public TKey LastKey
    {
        get; private set;
    }

    public TValue LastValue
    {
        get; private set;
    }
}

聪明 :-) 你可以使用具有私有设置器和公共获取器的自动属性使其更简洁。 - Metro Smurf
@Metro Smurf- 非常正确,谢谢。我已经更新了代码以包含这些内容- 我相信这会使示例更易于阅读。 - RichardOD
更好的方法是从Dictionary<TKey, TValue>继承,然后只需重写Add方法,在其中设置您添加的属性,然后调用base.Add(key, value)。这样可以节省大量手动委派Dictionary的所有函数的麻烦。 - sprite
2
@Sprite- 是的,继承字典会是更好的方法,但是Add不是虚拟的,所以你会遇到问题。 - RichardOD

1

来自文档

为了枚举的目的,字典中的每个项都被视为表示值和其键的KeyValuePair结构。返回项目的顺序是未定义的

因此,我认为您不能依赖Dictionary返回最后一个元素。

使用另一个集合。也许是SortedDictionary...


0
如果你只想要值,这个应该可以用(假设你可以使用LINQ):
dic.Values.Last()

你也可以使用 "dic.Last()" 来获取整个键/值对。它们应该是相同的。 - Dan Herbert

0

您可以使用以下方式:

dic.Last()

但是字典实际上没有最后一个元素(其中的键值对没有按任何特定方式排序)。最后一个项目将始终相同,但不明显它可能是哪个元素。


0

使用 .Net 3.5:

string lastItem = dic.Values.Last()
string lastKey = dic.Keys.Last()

...但请记住,字典是无序的,因此您不能指望值保持在相同的顺序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接