从数组创建列表的效率

5
我需要创建一个列表,其数据源是一个在之前创建的数组,而且我需要直接将数组转换成列表。因此,我希望通过直接引用数组来利用它,而不是复制一份——但是构建函数会复制一份。我可以理解这样做的动机。然而,有些情况下,我可以保证数组除了在它被创建的地方没有其他的引用。

是否有方法可以提高这个构造函数的效率,并在列表内部使用数组?我知道如果我不正确使用,就会产生问题。

最明显的例子就是获取string.Split()的结果。如果你需要一个列表,唯一明显的方法就是进行此转换。目前,我不考虑编写直接拆分为列表的方法。


@jdweng 它创建一个数组并将其转换为列表。 - Maniero
我认为你无法避免数组的复制。请参考 https://stackoverflow.com/questions/54298050/fastest-way-from-iterator-to-list-in-c-sharp/54298591#54298591。 - Johnny
@Johnny是我认识的人。但也许它是一个不太为人知的替代选择。 - Maniero
当您将一个对象添加到列表中时,它不会复制该对象。它使用指向原始对象的链接。 - jdweng
是的,那就是我想要说的。 - Vasek
显示剩余17条评论
2个回答

2
据我所知,没有官方的方法来实现这个,但是仍然可以使用 System.Reflection 来实现。通过查看 List<T> 的源代码,在 .NET Framework 4.7.2 中,两个重要的属性是 _items_size。还有一个属性是 _version,但只有在修改 List<T> 时才会更改。修改操作包括 AddAddRangeRemove 等,还包括 ReverseSort。因此,假设这与从 IEnumerable<T> 创建列表的操作相同,其中 _version 保持为零。最初的回答。
public static class ListExtensions
{
    public static void SetUnderlyingArray<T>(this List<T> list, T[] array)
    {
        lock (list)
        {
            SetInternalArray(list, array);
            SetInternalArraySize(list, array.Length);
        }
    }

    private static void SetInternalArraySize<T>(this List<T> list, int size)
    {
        var prop = list.GetType().GetField(
            "_size", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        prop.SetValue(list, size);
    }

    private static void SetInternalArray<T>(this List<T> list, T[] array)
    {
        var prop = list.GetType().GetField(
            "_items",
            BindingFlags.NonPublic | BindingFlags.Instance);
        prop.SetValue(list, array);
    }
}

and then set the underlying array

int[] array = Enumerable.Repeat(1, 1000000).ToArray();
List<int> list = new List<int>();

list.SetUnderlyingArray(array);

注意:此解决方案高度依赖于实现细节,如果List<T>内部发生变化可能是错误的,但它提供了如何实现的见解。

最初的回答


我会尝试。我不想使用这个解决方案,因为它依赖于细节。但可能这是唯一的选择。如果“_items”被设置为“protected”,那就容易多了。 - Maniero
@Maniero 没错。我已经声明了这个依赖项,我知道它的存在,并且我理解你的担忧... - Johnny

1
如果您不需要一个特定的List<T>,您可以创建一个实现IList<T>接口且不进行复制的新类。

这是一个备选方案,如果一切都失败了,我考虑过这个方案,它是一种激进的方式,但它是有效的。 - Maniero
我认为这比反射方法不那么激进。它不依赖于List<T>的实现。 - nloewen
是的,反射也很激进。 - Maniero

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