1

Why does .NET not provide a class for a List<KeyValuePair<T, U>>?

I think there are a lot of situations when you need to keep an array of pairs. For example,

1; 2      "a"; "array"
5; 8      "b"; "browser"
1; 9      "f"; "Firefox"
8; 10     "f"; "frequency"

À la:

Pairs<int, int> myPairs;

myPairs.Add(10, 8);
myPairs.Add(5, 8);
myPairs.Add(10, 5);
myPairs.Add(1, 4);

myPairs[0].Value1 = 5;
myPairs[5].Value2 = 8;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
serhio
  • 28,010
  • 62
  • 221
  • 374
  • 7
    ... why not just use `List>`? – user541686 Jan 26 '11 at 15:09
  • 1
    @Mehrdad: is a little "difficult" to build it. - I mean you should code a little building keyValuePairs, then aggregate them into a list. Finally why a List was built? You could use Arrays! :) – serhio Jan 26 '11 at 15:11
  • 1
    It's easier to use Tuple than KeyValuePair - and more appropriate too. – Jon Skeet Jan 26 '11 at 16:00
  • Could this be the same question as http://stackoverflow.com/questions/486948/linkedhashmap-in-net? It seems that that's what he needs, right? @Jon: Ever got round to a quick implementation? – wensveen Mar 04 '15 at 11:12
  • @wensveen: I don't see any indication that that's needed here - the OP didn't indicate that they need to look up by key. – Jon Skeet Mar 04 '15 at 11:50
  • @Jon: No, you're right. I think I was reading too much into the use of KeyValuePair. List of Tuples is the most correct answer then (and should be marked as such). I get the idea that serhio wasn't really even asking a question, but just frustrated about something missing in his opinion. – wensveen Mar 04 '15 at 20:41

5 Answers5

9

This seems completely unnecessary to me - and just because you have a pair of values doesn't mean it's necessarily a key/value relation either.

.NET 4 introduced the Tuple family of types... unless this really was a key/value pair, I'd use List<Tuple<T1, T2>> instead - and I see no reason for a single type to exist in order to encapsulate that construct.

EDIT: To put this into context, here's how "difficult" it is to use Tuple here, converting your sample code:

var myPairs = new List<Tuple<int, int>> {
    Tuple.Create(10, 8),
    Tuple.Create(5, 8),
    Tuple.Create(10, 5),
    Tuple.Create(1, 4),
};

Now tuples are immutable, so you wouldn't be able to set the values directly as per the last part of your code... but the beauty of composing the two concepts of "list" and "pair of values" is that you could easily write your own MutableTuple type and use exactly the same code... whereas if you had one collection class which was hard-wired to Tuple or some other type, you wouldn't have that flexibility.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes, there is no need to have relation between values. See topic edit as example. You could also use `Pairs` or whatever you want. – serhio Jan 26 '11 at 15:42
  • @serhio: In that case you don't want KeyValuePair, but `List>` should be fine for you. – Jon Skeet Jan 26 '11 at 15:44
  • your example will be a little less "beautiful" when adding items in a foreach in such a structure, or modifying its values. – serhio Jan 26 '11 at 15:51
  • @serhio: You'd have to give a more concrete example, but I expect it would still be perfectly fine. You could still use `Tuple.Create` which is far from awful... or you could use LINQ to generate the sequence in a more declarative manner to start with. – Jon Skeet Jan 26 '11 at 15:52
  • practical example: I need a collection of points(x, y) to pass to a function, without addind the namespace System.Drawing I am forced to use 2 lists of integers, or a Tuple, or a list of KeyValuePair... – serhio Jan 26 '11 at 16:21
  • @serhio: Yes, that is a practical example. But everyone has perfectly practical needs which are not universal; that's what you write your own types for. No one's arguing that *you* shouldn't write a collection type to encapsulate a list of 2-value points which meets your needs; this question is about why such a type doesn't exist in the BCL. Fortunately, *if* you need such a thing, as I keep saying, it is very easy to do. – Dan Tao Jan 26 '11 at 16:36
  • @serhio: Why would you not create your own Point type in that case? There you've got an obvious target for encapsulation: the two values are clearly related. Using Tuple or (worse) KeyValuePair would be wholly inappropriate IMO. – Jon Skeet Jan 26 '11 at 16:57
4

Why stop there? Why not a list of triplets? Or quadruplets?

The fact is that this is very trivial to write yourself. Write a class that derives from List<KeyValuePair<TKey, TValue>>, add an Add method that takes 2 parameters.

public class KeyValueList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>
{
    public void Add(TKey key, TValue value)
    {
        Add(new KeyValuePair<TKey, TValue>(key, value));
    }
}

Boom, you're done.


Further comments: Note that KeyValuePair<TKey, TValue> is an immutable type (as is Tuple<T1, T2> in .NET 4.0); so if you'd want to be able to do something like this:

list[0].Value = 5;

...then you'd need a mutable type, something like:

public sealed class Pair<T1, T2>
{
    public T1 X { get; set; }
    public T2 Y { get; set; }
}

public class PairList<T1, T2> : List<Pair<T1, T2>>
{ /* ...same as KeyValueList<T1, T2>, basically... */ }

Also note that this would enable nice initialization syntax:

var list = new PairList<int, string>
{
    { 1, "dan" },
    { 2, "serhio" }
};

But again, the point here is that this is very easy and very trivial and thus not really deserving of its own type in the framework. Look at it this way: can you name any type in the BCL that is essentially just another type but with one single convenience method added?


Closing comments: You have posed these questions:

  • Why introduce a List<T> type when you could just use a T[] array?
  • Why introduce a Dictionary<TKey, TValue> type when you could just use a List<KeyValuePair<TKey, TValue>>?

These are bizarre questions, honestly. Why introduce OOP at all when you can just do everything with procedural code and global variables? Why introduce higher-level programming languages like C#, C++, or even C when you could just write everything in Assembly?

The List<T> class provides a useful encapsulation of all the functionality that goes into accessing elements in an array, maintaining the number of items, resizing the array as necessary, etc. There is a big difference between what you can do with a List<T> and what you can do with just a T[].

The Dictionary<TKey, TValue> also encapsulates the functionality of maintaining a collection of values associated with unique keys. It also provides killer O(1) lookup time on those keys. There is a huge difference between a Dictionary<TKey, TValue> and a List<KeyValuePair<TKey, TValue>>.

The difference between a List<KeyValuePair<TKey, TValue>> and your proposed KeyValueList<TKey, TValue> (as I've called it above) is practically nil. Hardly anything new is encapsulated. It just is a List<T>. Honestly the benefit is, to me, hardly greater than adding an Int32List type that just wraps a List<int>.

There's just no reason to add such a type to the BCL.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 1
    is quite trivial to write a Dictionary yoursef too. However this class exists in .NET – serhio Jan 26 '11 at 15:23
  • 3
    @serhio: You think it's trivial to implement Dictionary properly and efficiently? I beg to differ. – Jon Skeet Jan 26 '11 at 15:24
  • 1
    and triplets are not so often used. The problem of multikeys dictionary is often asked, but only in .NET 3.5 the LookUp class appears. – serhio Jan 26 '11 at 15:25
  • @serhio: What?! It's trivial to write a hashtable yourself? I think that is a big stretch, compared to the code I just posted. – Dan Tao Jan 26 '11 at 15:26
  • 1
    @serhio: My point in mentioning triplets was just that the argument for incorporating such a type into the BCL is dwarfed (in my opinion) by the triviality of implementing your own type that does what you want. People have been making strongly-typed collections for their custom types since before generics (since before .NET, actually). It seems to me you are essentially asking why there is not a collection type for your specific type. That type just happens to be a generic key-value pair. – Dan Tao Jan 26 '11 at 15:31
  • so, OK, you've done a good implementation. So you think that is not so frequently used in order to include it in the Framework? – serhio Jan 26 '11 at 15:44
  • 2
    @serhio: no, it's not. What do you gain? A little bit of syntactic sugar. That's not enough of a plus to overcome the negatives of having a new class in the BCL that needs to be documented and tested. – thecoop Jan 26 '11 at 15:47
  • If the purpose of the list is to hold pairs of values, a `List>` would not be a terribly good fit, since each slot of such a list would not hold a `(T1,T2)` pair, but rather identify an object which not only holds such a pair, but *may be referenced elsewhere*. It would probably be better to use an array of a struct type, and manually handle the associated count and size issues, since e.g. given a `Point[] arr`, a statement like `arr[4].X += 5;` can be relied upon to safely update `arr[4].X` without affecting any other `Point` anywhere in the universe. – supercat Nov 20 '12 at 16:02
  • An alternative approach would be to define a class `MutableStructHolder where T:struct` with an exposed `Value` field *(not property)*, and which supports implicit casts to and from `T`, along with a `StructList` whose indexer accepts or returns a `MutableStructHolder` (the setter should create a new `MutableStructHolder` with data copied from the passed-in one). Given a `StructList list`, one could then say `list[4].Value.X += 5;` and know that it would only affect the one item's value. – supercat Nov 20 '12 at 16:08
  • Who you kidding? `C` IS assembly, for all intents and purposes (for the PDP-11) with some syntactic sugar thrown in for good measure. – Pieter Geerkens Mar 20 '13 at 16:30
3

What's stopping you from using a List<KeyValuePair<T, V>> directly? And, if you know T and V, you can alias it at the top of your source file:

using KeyValueList = System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<MyNS.MyKeyType, MyNS.MyValueType>>;

// in your code...
KeyValueList list = new KeyValueList();
list.Add(new KeyValuePair<MyKeyType, MyValueType>(..., ...));
thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 1
    see the answer to the remark. Why a List was built? You could use Arrays! :) – serhio Jan 26 '11 at 15:14
  • 1
    and, finally, the question is not that I can't use, but why is not present a more "commode" class. – serhio Jan 26 '11 at 15:17
  • 1
    `list.Add(new KeyValuePair(..., ...));` not so pretty. and in VB.NET worse that than. – serhio Jan 26 '11 at 15:18
  • I don't understand what you want. What do you want to do with a `KeyValueList` that you can't with a `List>`? – thecoop Jan 26 '11 at 15:18
  • @thecoop: My answer remains the same: Why you don't use an int[] but List ? Why a Dictionary class was created, when you just could use List? etc. – serhio Jan 26 '11 at 15:22
  • 1
    because a dictionary has an O(1) lookup time, whereas to get at a particular item in a list requires you to scan the list – thecoop Jan 26 '11 at 15:23
  • A List is a dynamic structure while an array in c# is a fixed length defined on creation. You can change the length by creating a new array and assigning it to the same variable but that requires copying of the whole array. Also, lists implement a number of methods not present in array. – David Mårtensson Jan 26 '11 at 15:28
  • 2
    @serhio: You realize a `Dictionary` is *not* just a wrapper for a list of key-value pairs, right? – Dan Tao Jan 26 '11 at 15:38
1

You may be looking for Dictionary<TKey,TValue>:

Represents a collection of keys and values.

Dictionary<TKey,TValue> is a specialized collection that is optimized for key/value pairs. While you are free to create a List<KeyValuePair<TKey,TValue>>, you are short-changing yourself as you will not have optimized access to your keys are values.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
0

You could use System.Collections.Specialized.NameValueCollection, that's a KeyValue store ;)

It's pure (string, string) mapping, but then most key value situations would work with that.

Or make a subclass of System.Collections.Specialized.NameObjectCollectionBase for a special solution for your object type.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Mårtensson
  • 7,550
  • 4
  • 31
  • 47
  • NameValueCollection is not generic. Have no Add method. – serhio Jan 26 '11 at 15:38
  • No but then, as soon as you go with generics there is an infinite number of possibilities. I have so many different combinations of list in my code that a special for Key Value would actually just complicate things, better to have a given solution that works for all. – David Mårtensson Jan 26 '11 at 15:40
  • don't understand the point. The scope of generics is to reduce your "possibilities" in a one single class: `Pairs` (see the example in my edit) – serhio Jan 26 '11 at 15:47
  • Then why complain that there is not a specialised class for this situation. Hashtable and Dictionary could handle most keyvalue situations and if you require something else you can design you own around existing solutions ;) – David Mårtensson Jan 27 '11 at 08:04