18 votos

¿Quién está en Dictionary<>.First()?

¿Cuál es el significado del método de extensión del .NET 3.5 Enumerable.First() cuando lo llamas en una instancia de la Dictionary colección?

¿El conjunto de claves determina qué artículo está primero, o simplemente no está definido?

14voto

Jon Skeet Puntos 692016

Bueno, creo que el juego de llaves será determinar qué artículo es el primero, pero no de una manera bien definida (o fácil de predecir). En otras palabras, no asuma que siempre funcionará de la misma manera - es tan inseguro como confiar en que la implementación de un código hash se mantenga igual entre las ejecuciones.

EDITORIAL: Creo que de hecho, el orden de inserción hace materia, contrariamente a mis ideas anteriores. Sin embargo, este es específico de la aplicación (por lo que podría cambiar fácilmente en la próxima versión). Creo que con la implementación actual, la primera entrada añadida será la primera devuelta si no se ha quitado. Si la primera entrada añadida es eliminada, el orden se rompe - no es que el más temprano la entrada es eliminada. Aquí hay un ejemplo:

using System;
using System.Collections.Generic;

class Test
{
    static void Main(string[] args)
    {
        var dict = new Dictionary<int, int>();        
        dict.Add(0, 0);
        dict.Add(1, 1);
        dict.Add(2, 2);
        dict.Remove(0);
        dict.Add(10, 10);

        foreach (var entry in dict)
        {
            Console.WriteLine(entry.Key);
        }
        Console.WriteLine("First key: " + dict.First().Key);
    }
}

Los resultados son 10, 1, 2, y "Primera clave: 10" - mostrando que el último la entrada añadida termina siendo devuelta primero.

Sin embargo, me gustaría subrayar de nuevo que todo puede cambiar entre las versiones del marco.

2voto

Shell User Puntos 31

Si necesitas el primer elemento de un diccionario, lo mejor es usar un SortedDictionary. Creo que el método First() devolverá el primer elemento que se encuentra en la parte superior, pero no necesariamente el primero que fue añadido.

2voto

Larry Fix Puntos 443

Estaba mirando un código que usaba un bucle de foreach para obtener el "primer" elemento en un objeto de diccionario. El código asume que este es el primero que se añade al diccionario.

Inicialmente pensé que el método Dictionary.First() sería más eficiente. Pero luego me di cuenta de que la noción de qué artículo es el primero podría no tener mucho sentido en este contexto.

El SortedDictionary, que Echilon sugirió, probablemente tiene más gastos generales y mucha más funcionalidad de la que necesito. Me inclino por guardar la clave del primer elemento añadido.

1voto

Robert Rossney Puntos 43767

El ordenamiento de la Keys colección en una clase que implementa Dictionary<TKey, TValue> no se especifica. Así que no sabes qué valor First() va a volver.

Pero hay una razón para usar First() de todos modos - o, más específicamente, para usar FirstOrDefault() . Si tienes un método que requiere un IEnumerable<T> y sabes que T es un tipo cuyo valor por defecto es null, your method can use Primero, el objeto se prueba para ver si está vacío.

¿Por qué harías esto en lugar de usar Count() ? Para aprovechar el aplazamiento de la ejecución. Si llamas a FirstOrDefault() en un generador, el generador da un resultado y se detiene. Si llama a Count() en un generador, el generador tiene que enumerar hasta el final de la lista.

Así que puedes escribir una función como esta:

bool ListIsEmpty(IEnumerable<string> list)
{
    return list.FirstOrDefault() == null;
}

y lo usan así:

if (!ListIsEmpty(dict.Keys)) 
{
    Console.WriteLine("Dictionary is not empty");
}
if (!ListIsEmpty(dict.Keys.Where(x => x.Contains("foo"))
{
    Console.WriteLine("Dictionary has at least one key containing 'foo'.");
}

y saber que el código está haciendo lo mínimo que tiene que hacer para tomar esas decisiones.

Editar:

Debo señalar que otra suposición que el código anterior está haciendo: que el IEnumerable<T> no tiene un nulo como primer artículo!

Esto siempre está garantizado para el Keys colección de un diccionario, o una DataRowCollection (mi caso de uso principal para LINQ), o para Where() cuando se ejecuta en una de esas colecciones.

Pero no está garantizado para un List<string> o un List<DataRow> . Así que definitivamente hay circunstancias en las que querrías pensártelo dos veces antes de usar FirstOrDefault() .

0voto

Larry Fix Puntos 443

Hice un poco más de investigación y encontré que MSDN advierte que el orden de los valores y las claves en un diccionario no está especificado. Así que creo que eso significa que First() no siempre puede devolver el mismo valor a medida que se añaden más valores.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X