如何快速将数组清零?

67

我目前正在使用for循环进行操作,在C语言中有ZeroMemory API,但是似乎在C#中并不可用。同样,Java中的Array.fill也没有相应的方法。我想知道是否有更简单/更快的方法?


2
你不需要在初始化期间清除它。你的帖子没有表明这是在初始化期间还是在将数据放入数组后。 - John Fisher
3
在我将数据放入数组之后。 - esac
4
达斯汀:我认为直接告诉我应该谷歌搜索是相当无礼的。显然,我的思维方式与你不同,而在搜索“C#中清空数组”时,并没有给我提供Array.Clear()方法。请问有其他建议吗? - esac
7个回答

122

尝试使用 Array.Clear() 方法:

该方法会将数组中指定范围内的元素设置为零、false 或 null(在 Visual Basic 中为 Nothing),具体取决于元素类型。


1
该描述似乎已过时,而且没有涵盖所有类型。它会根据元素类型将元素设置为其“默认”值。例如对于“char”,默认值将是:'\0' - Jogge

61
  • C++: memset(array, 0, array_length_in_bytes);

  • C++11: array.fill(0);

  • C#: Array.Clear(array, startingIndex, length);

  • Java: Arrays.fill(array, value);

上述代码是关于数组初始化的不同语言实现。其中,C++使用memset函数,通过将数组元素全部置为0来完成初始化;而C++11则提供了更加方便且易读的语法array.fill(0)。在C#中,可以使用Array.Clear方法来清空数组中指定范围内的元素;而在Java中,则可使用Arrays.fill方法来将指定数组中的所有元素均赋值为指定值。

11
我倾向于在C++中使用C++的std::fill而不是C函数memset。在优化代码中,memset通常会变成对库memset的调用,而std::fill只需要几个汇编指令,根本不需要调用。 - Hi-Angel

25

更新

根据关于Array.Clear()array[x] = default(T)性能的基准测试,我们可以得出以下两种情况需要考虑:

A) 数组长度为1到76个项

B) 数组长度大于等于77个项

因此,图中的橙色线条代表了Array.Clear()方法。

图中的蓝色线条代表了array[x] = default(T)方法(迭代数组并将其值设置为default(T))。

enter image description here

您可以编写一个Helper来完成此任务,例如:

public static class ArrayHelper
{
    // Performance-oriented algorithm selection
    public static void SelfSetToDefaults<T>(this T[] sourceArray)
    {
        if (sourceArray.Length <= 76)
        {
            for (int i = 0; i < sourceArray.Length; i++)
            {
                sourceArray[i] = default(T);
            }
        }
        else { // 77+
             Array.Clear(
                 array: sourceArray,
                 index: 0,
                 length: sourceArray.Length);
        }
    }
}

使用方法:

someArray.SelfSetToDefaults();

备注:

这个测试是针对 .NET Framework 4.x 进行的。

编辑(.NET6):

根据 Dai 的评论,在 .NET6 中,Array.Clear() 命令在 N > ~25 时现在更快了。


3
еңЁ.NET Framework 4.xдёӯпјҢеҪ“N > ~75ж—¶пјҢArray.Clear()жӣҙеҝ«пјҢдҪҶеңЁ.NET 6дёӯпјҢжҲ‘еҸ‘зҺ°N > ~25ж—¶пјҢArray.Clear()зҺ°еңЁжӣҙеҝ«гҖӮ - Dai

19
Array.Clear(integerArray, 0, integerArray.Length);

15

有几个人发布了答案,然后删除了它们,说无论用什么语言,for循环与memset或FillMemory等方法在性能上是相同的。

例如,编译器可能将其划分为64位对齐的块,以利用64位零赋值指令(如果有的话)。它将考虑对齐和其他因素。Memset的实现肯定不是简单的。

参见 memset.asmmemset-is-faster-than-simple-loop.html 来查看示例。

永远不要低估编译器和标准库作者的巧妙程度。


12
我使用了for循环和Array.Clear()来进行测试。在一个包含4K元素的数组上,用循环执行了200万次Array.Clear(),耗时620毫秒。而使用for循环则需要13030毫秒。 - esac
2
@esac 虽然这确实回答了你的问题,但对于所有操作系统和硬件是否仍然如此仍然是一个未决问题。我同意Array.Clear()至少与for循环一样快,可能更快,这使得Array.Clear()成为明显的选择。(而且,它更容易阅读。) - Richard
@Richard 对于非常小的数组(1 <= N < ~25-75),Array.Clear()for 循环慢,这是因为 Array.Clear() 内部逻辑需要进行参数验证和获取数组的维度/边界。 - Dai

5
使用dll导入调用该方法。这种方法快速易用 :)
 [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
 public static extern IntPtr MemSet(IntPtr dest, int c, int byteCount);

c是您想在内存中设置的值。

或者

[DllImport("kernel32.dll", EntryPoint="RtlZeroMemory")]
public unsafe static extern bool ZeroMemory(byte* destination, int length);

这仅将给定的数组设置为零。


0

我相信这就是你要找的,我用Visual Basic写的,但我相信你可以转换它。

Imports System.Runtime.InteropServices

Module Module1

    'import Kernel32 so we can use the ZeroMemory Windows API function
    <DllImport("kernel32.dll")>
    Public Sub ZeroMemory(ByVal addr As IntPtr, ByVal size As IntPtr)

    End Sub

    Private Sub ZeroArray(array As ArrayList)
        'Iterate from 0 to the lenght of the array zeroing each item at that index
        For i As Integer = 0 To array.Count - 1
            'Pin the array item in memory
            Dim gch As GCHandle = GCHandle.Alloc((array(i)), GCHandleType.Pinned)
            'Get the memory address of the object pinned
            Dim arrayAddress As IntPtr = gch.AddrOfPinnedObject()
            'Get size of the array
            Dim arraySize As IntPtr = CType(array.Count, IntPtr)
            'Zero memory at the current index address in memory
            ZeroMemory(arrayAddress, arraySize)
            gch.Free()
        Next

    End Sub


    Sub Main()
        'Initialize ArrayList with items
        Dim strArray As New ArrayList From {
            "example one",
            "example two",
            "example three"
        }

        'Pass array as parameter to a function which will iterate through the arraylist zeroing each item in memory
        ZeroArray(strArray)

        Console.ReadLine()
    End Sub

End Module

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