Getting a sub-array from an existing array
我有一个由10个元素组成的数组x。我想创建一个新数组,包含从x开始的所有元素,这些元素从索引3开始,到索引7结束。当然,我可以很容易地编写一个循环,它将为我做这件事,但我想保持我的代码尽可能干净。C中有什么方法可以帮我做吗?
类似(伪代码):
1 | Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex) |
您可以将其添加为扩展方法:
1 2 3 4 5 6 7 8 9 10 11 | 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} } |
号
更新重新克隆(在最初的问题中不明显)。如果你真的想要一个深度克隆,比如:
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } } |
但这确实要求对象是可序列化的(
请注意,深度克隆在没有序列化的情况下很困难;特别是,在大多数情况下,
创建新数组后,可以使用
如果使用.NET 3.5,则可以使用Linq:
1 | var newArray = array.Skip(3).Take(5).ToArray(); |
但这样做效率会降低。
有关更具体情况的选项,请参阅类似问题的回答。
你考虑过使用
http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx
以下代码在一行中执行此操作:
1 2 3 4 |
号
我知道你想克隆,而不仅仅是复制参考文献。在这种情况下,可以使用
1 | var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray(); |
注意:此解决方案需要.NET Framework 3.5。
1 2 3 | string[] arr = {"Parrot" ,"Snake" ,"Rabbit" ,"Dog" ,"cat" }; arr = arr.ToList().GetRange(0, arr.Length -1).ToArray(); |
号
基于Marc的答案,但添加了所需的克隆行为
1 2 3 4 5 6 7 8 9 10 11 | 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的反射式方法是什么呢?瓦尔德斯特兰登的可复制的图书馆做所需的重型起重。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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实现可以与以下任何一种实现一起工作:
For the automated copy to work, though, one of the following statements must hold for instance:
- Its type must have a parameterless constructor, or
- It must be a Copyable, or
- It must have an IInstanceProvider registered for its type.
号
所以这应该涵盖几乎所有的情况。如果要克隆子图中包含诸如db连接或文件/流处理等内容的对象,显然会出现问题,但对于任何通用的深度复制都是如此。
如果您想使用其他的深度复制方法,本文将列出其他一些方法,因此我建议您不要尝试编写自己的方法。
你可以很容易地做到这一点;
我认为您要查找的代码是:
埃多克斯1〔6〕
在C 8中,他们引入了新的
1 2 3 4 | int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Index i1 = 3; // number 3 from beginning Index i2 = ^4; // number 4 from end var slice = a[i1..i2]; // { 3, 4, 5 } |
号
作为复制数据的替代方法,您可以制作一个包装器,使您可以像复制数组的一部分一样访问原始数组的一部分。优点是,您不会在内存中获得数据的另一个副本,缺点是在访问数据时开销很小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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(); } } |
。
用途:
1 2 3 4 5 6 | 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 |
。
array.constrainedCopy将工作。
1 2 3 4 5 6 7 | public static void ConstrainedCopy ( Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length ) |
。
没有一种方法可以满足你的需要。您需要为数组中的类提供一个克隆方法。那么,如果LINQ是一个选项:
1 2 3 4 5 6 7 8 9 | Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray(); class Foo { public Foo Clone() { return (Foo)MemberwiseClone(); } } |
。
我不确定它到底有多深,但是:
埃多克斯1〔10〕
这有点开销,但可能会减少不必要的方法。
使用array.constrainedCopy如何:
1 2 3 |
号下面是我原来的帖子。它不起作用
您可以使用array.copyto:
1 2 3 4 |
号
这个怎么样:
1 2 3 4 5 6 7 8 9 10 | 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接口,但这应该可以做到。
至于克隆,我认为序列化不会调用构造函数。如果在ctor中进行有趣的操作,这可能会破坏类不变量。
虚拟克隆方法调用复制构造函数似乎更安全。
1 2 3 4 5 6 7 8 9 | protected MyDerivedClass(MyDerivedClass myClass) { ... } public override MyBaseClass Clone() { return new MyDerivedClass(this); } |
号
使用扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public static T[] Slice<T>(this T[] source, int start, int end) { // Handles negative ends. if (end < 0) { end = source.Length + end; } int len = end - start; // Return new array. T[] res = new T[len]; for (int i = 0; i < len; i++) { res[i] = source[i + start]; } return res; } |
你可以用它
1 | var NewArray = OldArray.Slice(3,7); |
。
你可以参加微软制造的课程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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; } } |
号
然后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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)); } } |
我发现,这是实现这一目标的最佳方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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]); } } |
希望有帮助!
在一个数组中克隆元素并不是一件可以用通用方法完成的事情。您要进行深度克隆还是复制所有成员的简单副本?
让我们开始"尽最大努力"的方法:使用ICloneable接口或二进制序列化克隆对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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); } } } |
号
这不是一个完美的解决方案,因为对于任何类型的对象来说,根本没有一个是有效的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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(); } |
号