It is simple. But I am not sure if it will be faster it needs to be tested,
var dic = new Dictionary<int, string>();
for (int i = 0; i < 20000; i++)
{
dic.Add(i, i.ToString());
}
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Key); //.ToList();
var list2 = (from item in dic
join listItem in list
on item.Key equals listItem
select item).ToList();
Here I have run profiler using the code below:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
const int ResultLength = 11111;
const int NElements = 20000;
static readonly int[] RandomArrayOrderCache = RandomArrayOrder();
static readonly Dictionary<int, string> dic = Enumerable.Range(0, NElements).ToDictionary(k => k, v => v.ToString());
static readonly KeyValuePair<int, string>[] UnorderedList =
dic.OrderBy((k) => RandomArrayOrderCache[k.Key]).ToArray();
static int[] RandomArrayOrder()
{
var result = Enumerable.Range(0, NElements).ToArray();
var random = new Random();
for (int i = 0; i <= 10000000; i++)
{
var i1 = random.Next(0, result.Length - 1);
var i2 = random.Next(0, result.Length - 1);
var tmp = result[i1];
result[i1] = result[i2];
result[i2] = tmp;
}
return result;
}
public static List<KeyValuePair<int, string>> TestWithoutToListUsingJoin()
{
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Key);// .ToList();
return (from item in dic
join listItem in list
on item.Key equals listItem
select item).ToList();
}
public static List<KeyValuePair<int, string>> TestWithToListUsingJoin()
{
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Key).ToList();
return (from item in dic
join listItem in list
on item.Key equals listItem
select item).ToList();
}
public static List<KeyValuePair<int, string>> TestWithToListUsingJoinButRandomizeElementOrder()
{
var list = UnorderedList.Where(f => f.Value.StartsWith("1")).Select(f => f.Key).ToList();
return (from item in dic
join listItem in list
on item.Key equals listItem
select item).ToList();
}
public static List<KeyValuePair<int, string>> TestWithoutToListUsingJoinButRandomizeElementOrder()
{
var list = UnorderedList.Where(f => f.Value.StartsWith("1")).Select(f => f.Key);
return (from item in dic
join listItem in list
on item.Key equals listItem
select item).ToList();
}
public static List<KeyValuePair<int, string>> TestWithToListOldWay()
{
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Key).ToList();
return dic.Where(f => list.Contains(f.Key)).ToList();
}
public static List<KeyValuePair<int, string>> TestWithoutToListOldWay()
{
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Key);// .ToList();
return dic.Where(f => list.Contains(f.Key)).ToList();
}
public static List<KeyValuePair<int, string>> TestWithoutToListIfItWasUsedWithAReferenceType()
{
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Value);// .ToList();
return (from item in dic
join listItem in list
on item.Value equals listItem
select item).ToList();
}
public static List<KeyValuePair<int, string>> TestWithToListIfItWasUsedWithAReferenceType()
{
var list = dic.Where(f => f.Value.StartsWith("1")).Select(f => f.Value).ToList();
return (from item in dic
join listItem in list
on item.Value equals listItem
select item).ToList();
}
public static void Main(string[] args)
{
long dummy = 0;
long numberOfRetries = 1000;
for (int i = 0; i < numberOfRetries; ++i)
{
dummy += TestWithoutToListUsingJoin().Count;
dummy += TestWithToListUsingJoin().Count;
dummy += TestWithToListOldWay().Count;
// This takes ridicilous amount of time so I am disabling it from this test to compare others better
//dummy += TestWithoutOldWay().Count;
// Added these two to compare the heap vs stack caching effects on times
dummy += TestWithoutToListIfItWasUsedWithAReferenceType().Count;
dummy += TestWithToListIfItWasUsedWithAReferenceType().Count;
dummy += TestWithToListUsingJoinButRandomizeElementOrder().Count;
dummy += TestWithoutToListUsingJoinButRandomizeElementOrder().Count;
}
Environment.Exit(dummy == ResultLength * 7 * numberOfRetries ? 0 : 1);
}
}
Here are the results:

From the results we can see the reason why. Because the first list where you are using on the where clause is a struct list.So it benefits greatly from the CPU cache. But when we use a reference type where the instances spread across the heap then we see the benfit diminishes. Same result we can see on randomly spread keys too. The performance impact you gain using ToList is ordered values in contiguous memory hitting CPU cache mostly,