4

C#'s List<> has a set of CopyTo functions that will extract the contents of its internal array into another array using a fast memory block copy.

Is there a way to do this in reverse? It might look like...

var buffer = new List<byte>();
buffer.AddRange(afewbytes);
buffer.AddFromArray(myArray, startIndex, countBytesToCopy);
buffer.AddRange(afewmorebytes);

As my List is the List<byte> variety, I'd prefer to avoid a loop that copies byte by byte.

billpg
  • 3,195
  • 3
  • 30
  • 57

3 Answers3

7

The List<T>(IEnumerable<T>) constructor will use ICollection<T>.CopyTo if the collection implements ICollection<T>, which byte[] will do.

That's not going to help directly if you only want to extract part of the array, but you could create your own ByteArraySegment class implementing ICollection<byte> and implement the CopyTo operation using Buffer.BlockCopy or whatever:

public class ByteArraySegment : ICollection<byte>
{ 
    private readonly byte[] array;
    private readonly int start;
    private readonly int count;

    public ByteArraySegment(...)
    {
        // Obvious code
    }

    public void CopyTo(byte[] target, int index)
    { 
        Buffer.BlockCopy(array, start, target, index, count);
    }

    // Other ICollection<T> members
}

Then:

List<byte> bytes = new List<byte>(new ByteArraySegment(myArray, start, count));

(Or use AddRange which has the same optimization.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Many thanks! I've edited my question to clarify I'm looking for an efficient Add rather than a constructor, but from the looks of the comments on David's answer, your class should work for AddRange too. – billpg May 01 '14 at 17:43
  • Are we really sure that we can trust this Jon Skeet chap? ;) – Brandon May 01 '14 at 17:46
  • You can never be sure with Jon Skeet... some say, he's not even real! – rae1 May 01 '14 at 17:49
4

To copy part of an array, wrap the array in an ArraySegment, specifying the index and count of the segment. Add the ArraySegment to the list with the list's AddRange method. AddRange will use ArraySegment.CopyTo, which uses Array.Copy, which is fast.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
3

List.AddRange(myArray) should be pretty efficient.

From MSDN: "If the new Count (the current Count plus the size of the collection) will be greater than Capacity, the capacity of the List is increased by automatically reallocating the internal array to accommodate the new elements, and the existing elements are copied to the new array before the new elements are added."

Haney
  • 32,775
  • 8
  • 59
  • 68
  • List.AddRange takes an IEnumerable as a parameter. Isn't that going to handle each byte one-by-one? (Instead of as a block) – billpg May 01 '14 at 17:37
  • 2
    Nope, it will interpret it as an ICollection and perform an Array.Copy which is as fast as memcpy (it probably uses memcpy under the hood). – Asik May 01 '14 at 17:38
  • 1
    @Asik, AddRange combined with the code in Jon's answer should do the trick then. Many thanks! – billpg May 01 '14 at 17:47