从现有数组获取子数组
我有10个元素的数组X. 我想创build一个新的数组,其中包含从索引3开始到索引7结束的X中的所有元素。当然,我可以轻松地编写一个循环,这将为我做,但我想保持我的代码尽可能干净。 在C#中有一种方法可以为我做吗?
像(伪代码):
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy
不适合我的需求 。 我需要新数组中的项目是克隆。 Array.copy
只是一个C风格的memcpy
等价物,这不是我正在寻找的。
您可以将其添加为扩展方法:
public static T[] SubArray<T>(this T[] data, int index, int length) { T[] result = new T[length]; Array.Copy(data, index, result, 0, length); return result; } static void Main() { int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int[] sub = data.SubArray(3, 4); // contains {3,4,5,6} }
更新克隆(在原始问题中不明显)。 如果你真的想要一个深刻的克隆; 就像是:
public static T[] SubArrayDeepClone<T>(this T[] data, int index, int length) { T[] arrCopy = new T[length]; Array.Copy(data, index, arrCopy, 0, length); using (MemoryStream ms = new MemoryStream()) { var bf = new BinaryFormatter(); bf.Serialize(ms, arrCopy); ms.Position = 0; return (T[])bf.Deserialize(ms); } }
这确实需要对象是可序列化的( [Serializable]
或ISerializable
)。 您可以很容易地replace任何其他序列化器 – XmlSerializer
, DataContractSerializer
,protobuf网等
请注意,深度克隆是棘手的,没有序列化; 特别是ICloneable
在大多数情况下很难相信。
您可以使用Array.Copy(...)
复制到新的数组后创build它,但我不认为有创build新的数组并复制一系列元素的方法。
如果您使用.NET 3.5,则可以使用LINQ:
var newArray = array.Skip(3).Take(5).ToArray();
但是效率会有所下降。
看到这个答案类似的问题的更多具体情况的选项。
你有没有考虑过使用ArraySegment
?
我看到你想做克隆,而不仅仅是复制引用。 在这种情况下,您可以使用.Select将数组成员投影到他们的克隆。 例如,如果你的元素实现了IClonable,你可以这样做:
var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
下面的代码在一行中完成:
// Source array string[] Source = new string[] { "A", "B", "C", "D" }; // Extracting a slice into another array string[] Slice = new List<string>(Source).GetRange(2, 2).ToArray();
build立在马克的答案,但增加了所需的克隆行为
public static T[] CloneSubArray<T>(this T[] data, int index, int length) where T : ICloneable { T[] result = new T[length]; for (int i = 0; i < length; i++) { var original = data[index + i]; if (original != null) result[i] = (T)original.Clone(); return result; }
而如果实施ICloneable太像辛勤工作,反思一个使用HåvardStranden的可复制库进行所需的繁重工作。
using OX.Copyable; public static T[] DeepCopySubArray<T>( this T[] data, int index, int length) { T[] result = new T[length]; for (int i = 0; i < length; i++) { var original = data[index + i]; if (original != null) result[i] = (T)original.Copy(); return result; }
请注意,OX.Copyable实现适用于以下任何一种:
对于自动化副本的工作,虽然,下面的一个声明必须举例说明:
- 它的types必须有一个无参数的构造函数,或者
- 它必须是一个可复制的,或者
- 它必须有一个IInstanceProvider注册它的types。
所以这应该包括几乎所有的情况。 如果您要克隆的子对象包含数据库连接或文件/stream处理等对象,则显然存在问题,但对于任何通用深度复制都是如此。
如果你想使用其他深层复制的方法,而不是本文列出的其他几个人,所以我build议不要试图写自己的。
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" }; arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
你可以很简单地做到这一点。
object[] foo = new object[10]; object[] bar = new object[7]; Array.Copy(foo, 3, bar, 0, 7);
Array.ConstrainedCopy将工作。
public static void ConstrainedCopy ( Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length )
我认为你正在寻找的代码是:
Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)
作为复制数据的替代方法,您可以创build一个包装器,使您可以访问原始数组的一部分,就好像它是数组部分的副本一样。 好处是你不会在内存中获得另一个数据副本,而访问这些数据的缺点是很小的开销。
public class SubArray<T> : IEnumerable<T> { private T[] _original; private int _start; public SubArray(T[] original, int start, int len) { _original = original; _start = start; Length = len; } public T this[int index] { get { if (index < 0 || index >= Length) throw new IndexOutOfRangeException(); return _original[_start + index]; } } public int Length { get; private set; } public IEnumerator<T> GetEnumerator() { for (int i = 0; i < Length; i++) { yield return _original[_start + i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
用法:
int[] original = { 1, 2, 3, 4, 5 }; SubArray<int> copy = new SubArray<int>(original, 2, 2); Console.WriteLine(copy.Length); // shows: 2 Console.WriteLine(copy[0]); // shows: 3 foreach (int i in copy) Console.WriteLine(i); // shows 3 and 4
没有一种方法可以做你想做的事情。 您将需要为您的数组中的类提供克隆方法。 那么,如果LINQ是一个选项:
Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray(); class Foo { public Foo Clone() { return (Foo)MemberwiseClone(); } }
如何使用Array.ConstrainedCopy :
int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8}; int[] ArrayTwo = new int[5]; Array.ConstrainedCopy(ArrayOne, 3, ArrayTwo, 0, 7-3);
以下是我原来的post。 不起作用
你可以使用Array.CopyTo :
int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8}; int[] ArrayTwo = new int[5]; ArrayOne.CopyTo(ArrayTwo,3); //starts copy at index=3 until it reaches end of //either array
这个怎么样:
public T[] CloneCopy(T[] array, int startIndex, int endIndex) where T : ICloneable { T[] retArray = new T[endIndex - startIndex]; for (int i = startIndex; i < endIndex; i++) { array[i - startIndex] = array[i].Clone(); } return retArray; }
然后,需要在所有需要使用此类的类上实现ICloneable接口,但应该这样做。
我不确定它有多深,但是:
MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()
这是一个开销,但它可能会削减一个不必要的方法。
就克隆而言,我不认为序列化会调用你的构造函数。 如果你正在做有趣的事情,这可能会破坏类不variables。
似乎更安全的赌注是虚拟克隆方法调用复制构造函数。
protected MyDerivedClass(MyDerivedClass myClass) { ... } public override MyBaseClass Clone() { return new MyDerivedClass(this); }
数组中的克隆元素不是通用的方式。 你想要深刻的克隆或所有成员的简单副本?
让我们去“尽力而为”的方法:使用ICloneable接口或二进制序列化克隆对象:
public static class ArrayExtensions { public static T[] SubArray<T>(this T[] array, int index, int length) { T[] result = new T[length]; for (int i=index;i<length+index && i<array.Length;i++) { if (array[i] is ICloneable) result[i-index] = (T) ((ICloneable)array[i]).Clone(); else result[i-index] = (T) CloneObject(array[i]); } return result; } private static object CloneObject(object obj) { BinaryFormatter formatter = new BinaryFormatter(); using (MemoryStream stream = new MemoryStream()) { formatter.Serialize(stream, obj); stream.Seek(0,SeekOrigin.Begin); return formatter.Deserialize(stream); } } }
这不是一个完美的解决scheme,因为没有任何东西可以用于任何types的对象。
你可以参加微软的课程:
internal class Set<TElement> { private int[] _buckets; private Slot[] _slots; private int _count; private int _freeList; private readonly IEqualityComparer<TElement> _comparer; public Set() : this(null) { } public Set(IEqualityComparer<TElement> comparer) { if (comparer == null) comparer = EqualityComparer<TElement>.Default; _comparer = comparer; _buckets = new int[7]; _slots = new Slot[7]; _freeList = -1; } public bool Add(TElement value) { return !Find(value, true); } public bool Contains(TElement value) { return Find(value, false); } public bool Remove(TElement value) { var hashCode = InternalGetHashCode(value); var index1 = hashCode % _buckets.Length; var index2 = -1; for (var index3 = _buckets[index1] - 1; index3 >= 0; index3 = _slots[index3].Next) { if (_slots[index3].HashCode == hashCode && _comparer.Equals(_slots[index3].Value, value)) { if (index2 < 0) _buckets[index1] = _slots[index3].Next + 1; else _slots[index2].Next = _slots[index3].Next; _slots[index3].HashCode = -1; _slots[index3].Value = default(TElement); _slots[index3].Next = _freeList; _freeList = index3; return true; } index2 = index3; } return false; } private bool Find(TElement value, bool add) { var hashCode = InternalGetHashCode(value); for (var index = _buckets[hashCode % _buckets.Length] - 1; index >= 0; index = _slots[index].Next) { if (_slots[index].HashCode == hashCode && _comparer.Equals(_slots[index].Value, value)) return true; } if (add) { int index1; if (_freeList >= 0) { index1 = _freeList; _freeList = _slots[index1].Next; } else { if (_count == _slots.Length) Resize(); index1 = _count; ++_count; } int index2 = hashCode % _buckets.Length; _slots[index1].HashCode = hashCode; _slots[index1].Value = value; _slots[index1].Next = _buckets[index2] - 1; _buckets[index2] = index1 + 1; } return false; } private void Resize() { var length = checked(_count * 2 + 1); var numArray = new int[length]; var slotArray = new Slot[length]; Array.Copy(_slots, 0, slotArray, 0, _count); for (var index1 = 0; index1 < _count; ++index1) { int index2 = slotArray[index1].HashCode % length; slotArray[index1].Next = numArray[index2] - 1; numArray[index2] = index1 + 1; } _buckets = numArray; _slots = slotArray; } internal int InternalGetHashCode(TElement value) { if (value != null) return _comparer.GetHashCode(value) & int.MaxValue; return 0; } internal struct Slot { internal int HashCode; internal TElement Value; internal int Next; } }
接着
public static T[] GetSub<T>(this T[] first, T[] second) { var items = IntersectIteratorWithIndex(first, second); if (!items.Any()) return new T[] { }; var index = items.First().Item2; var length = first.Count() - index; var subArray = new T[length]; Array.Copy(first, index, subArray, 0, length); return subArray; } private static IEnumerable<Tuple<T, Int32>> IntersectIteratorWithIndex<T>(IEnumerable<T> first, IEnumerable<T> second) { var firstList = first.ToList(); var set = new Set<T>(); foreach (var i in second) set.Add(i); foreach (var i in firstList) { if (set.Remove(i)) yield return new Tuple<T, Int32>(i, firstList.IndexOf(i)); } }
这是我find的最佳方式:
private void GetSubArrayThroughArraySegment() { int[] array = { 10, 20, 30 }; ArraySegment<int> segment = new ArraySegment<int>(array, 1, 2); Console.WriteLine("-- Array --"); int[] original = segment.Array; foreach (int value in original) { Console.WriteLine(value); } Console.WriteLine("-- Offset --"); Console.WriteLine(segment.Offset); Console.WriteLine("-- Count --"); Console.WriteLine(segment.Count); Console.WriteLine("-- Range --"); for (int i = segment.Offset; i <= segment.Count; i++) { Console.WriteLine(segment.Array[i]); } }
希望它有助于!
public static T[] SubArray<T>(T[] data, int index, int length) { List<T> retVal = new List<T>(); if (data == null || data.Length == 0) return retVal.ToArray(); bool startRead = false; int count = 0; for (int i = 0; i < data.Length; i++) { if (i == index && !startRead) startRead = true; if (startRead) { retVal.Add(data[i]); count++; if (count == length) break; } } return retVal.ToArray(); }