Diferencia entre Lookup () y Dictionary (Of list ())

165

Estoy tratando de entender qué estructuras de datos son las más eficientes y cuándo / dónde usar cuáles.

Ahora, podría ser que simplemente no entiendo las estructuras lo suficientemente bien, pero ¿cómo es ILookup(of key, ...)diferente de a Dictionary(of key, list(of ...))?

Además, ¿dónde me gustaría usar un ILookupy dónde sería más eficiente en términos de velocidad del programa / memoria / acceso a datos, etc.?

John Bustos
fuente
1
uno también puede querer ver cuál es el punto de búsqueda
teletexto

Respuestas:

255

Dos diferencias significativas:

  • LookupEs inmutable. Yay :) (Al menos, creo que la Lookupclase concreta es inmutable, y la ILookupinterfaz no proporciona ningún miembro mutante. Por supuesto, podría haber otras implementaciones mutables).
  • Cuando busca una clave que no está presente en una búsqueda, obtiene una secuencia vacía en lugar de a KeyNotFoundException. (Por lo tanto, no hay TryGetValue, AFAICR).

Es probable que sean equivalentes en eficiencia: la búsqueda bien puede usar un Dictionary<TKey, GroupingImplementation<TValue>>detrás de escena, por ejemplo. Elija entre ellos según sus requisitos. Personalmente, encuentro que la búsqueda suele ser más adecuada que a Dictionary<TKey, List<TValue>>, principalmente debido a los dos primeros puntos anteriores.

Tenga en cuenta que, como detalle de implementación, IGrouping<,>cuya implementación concreta se utiliza para los implementos de valores IList<TValue>, lo que significa que es eficiente de usar Count(), ElementAt()etc.

Jon Skeet
fuente
Si una búsqueda de clave inexistente da como resultado una secuencia vacía en lugar de una excepción, entonces no se puede usar como una colección de propósito general. Está bastante bien en el caso de una colección inmutable que es un subproducto de las consultas linq.
nawfal
@nawfal: para eso son exactamente las búsquedas. De msdn : "Puede crear una instancia de Lookup <TKey, TElement> llamando a ToLookup en un objeto que implementa IEnumerable <T>".
Niall Connaughton
50

Es interesante que nadie haya declarado la mayor diferencia real (tomada directamente de MSDN ):

Una búsqueda se asemeja a un diccionario. La diferencia es que un Diccionario asigna claves a valores individuales, mientras que una Búsqueda asigna claves a colecciones de valores.

Mladen Mihajlovic
fuente
51
Verifique la pregunta: se trata de la diferencia entre una Búsqueda <TKey, TValue> y un Diccionario <TKey, List <TValue>>, por lo que esa diferencia ya es bastante explícita.
Martao
55
@Martao, algunas personas encuentran esta pregunta cuando buscan en Google para comprender la diferencia entre las búsquedas y los diccionarios. Esta respuesta es realmente útil.
jakubiszon
34

Tanto a Dictionary<Key, List<Value>>como a Lookup<Key, Value>lógicamente pueden contener datos organizados de manera similar y ambos son del mismo orden de eficiencia. La principal diferencia es que Lookupes inmutable: no tiene Add()métodos ni constructor público (y, como Jon mencionó, puede consultar una clave inexistente sin excepción y tener la clave como parte de la agrupación).

En cuanto a cuál usa, realmente depende de cómo quiera usarlos. Si mantiene un mapa de clave a múltiples valores que se modifica constantemente, entonces Dictionary<Key, List<Value>>probablemente sea mejor ya que es mutable.

Sin embargo, si tiene una secuencia de datos y solo desea una vista de solo lectura de los datos organizados por clave, entonces una búsqueda es muy fácil de construir y le dará una instantánea de solo lectura.

James Michael Hare
fuente
11

La principal diferencia entre an ILookup<K,V>y a Dictionary<K, List<V>>es que un diccionario es mutable; puede agregar o eliminar claves, y también agregar o eliminar elementos de la lista que se busca. Un ILookupes inmutable y no se puede modificar una vez creado.

La implementación subyacente de ambos mecanismos será la misma o similar, por lo que su velocidad de búsqueda y huella de memoria será aproximadamente la misma.

Servy
fuente
1
@JohnBustos En términos de rendimiento, no. Es puramente lógico. Puede pasar referencias a la estructura en torno a no preocuparse de que alguien más la modifique por debajo de usted. Puedes hacer suposiciones sobre el hecho de que es inmutable que no podrías si fuera mutable.
Servicio
Gracias, Servy, ese es un muy buen punto cuando pasas tantas variables ByRef a menudo, al menos esta seguro que no se puede modificar. ¡Gracias!
John Bustos el
2
@JohnBustos Tenga en cuenta que el método predeterminado para pasar un parámetro de método es por valor, debe agregar explícitamente byref, y eso es algo que debe hacerse con bastante poca frecuencia. Estas estructuras de datos son clases, lo que las convierte en tipos de referencia, por lo que pasar el valor es el valor de la referencia, por lo que pasarlo a otro método puede causar cambios visibles en la persona que llama.
Servicio
Gracias, Servy, eso me abre una nueva lata de gusanos en términos de lo que he estado haciendo :), pero entiendo lo que estás diciendo. ¡¡Gracias!!
John Bustos el
Debajo de las cubiertas, ¿sabe si Lookup usa hashbuckets para la clave?
paparazzo
10

Otra diferencia que aún no se menciona es que Lookup () admite claves nulas :

La clase de búsqueda implementa la interfaz ILookup. La búsqueda es muy similar a un diccionario, excepto que se permite asignar múltiples valores a la misma clave y se admiten claves nulas.

Noble_Bright_Life
fuente
4

Cuando la excepción no es una opción, vaya a Buscar

Si está tratando de obtener una estructura tan eficiente como una Dictionarypero no sabe con certeza que no hay una clave duplicada en la entrada, Lookupes más seguro.

Como se menciona en otra respuesta, también admite claves nulas, y siempre devuelve un resultado válido cuando se consulta con datos arbitrarios, por lo que parece más resistente a la entrada desconocida (menos propenso que el Diccionario para generar excepciones).

Y es especialmente cierto si lo compara con la System.Linq.Enumerable.ToDictionaryfunción:

// won't throw
new[] { 1, 1 }.ToLookup(x => x); 

// System.ArgumentException: An item with the same key has already been added.
new[] { 1, 1 }.ToDictionary(x => x);

La alternativa sería escribir su propio código duplicado de administración de claves dentro de un foreachbucle.

Consideraciones de rendimiento, Diccionario: un claro ganador

Si no necesita una lista y va a administrar una gran cantidad de elementos, Dictionary(o incluso su propia estructura personalizada) sería más eficiente:

        Stopwatch stopwatch = new Stopwatch();
        var list = new List<string>();
        for (int i = 0; i < 5000000; ++i)
        {
            list.Add(i.ToString());
        }
        stopwatch.Start();
        var lookup = list.ToLookup(x => x);
        stopwatch.Stop();
        Console.WriteLine("Creation: " + stopwatch.Elapsed);

        // ... Same but for ToDictionary
        var lookup = list.ToDictionary(x => x);
        // ...

Como Lookupdebe mantener una lista de elementos para cada tecla, es más lenta que Dictionary (alrededor de 3 veces más lenta para una gran cantidad de elementos)

Velocidad de búsqueda: Creación: 00: 00: 01.5760444

Velocidad del diccionario: Creación: 00: 00: 00.4418833

Fabuloso
fuente