关于c#:从现有数组中获取子数组

Getting a sub-array from an existing array

我有一个由10个元素组成的数组x。我想创建一个新数组,包含从x开始的所有元素,这些元素从索引3开始,到索引7结束。当然,我可以很容易地编写一个循环,它将为我做这件事,但我想保持我的代码尽可能干净。C中有什么方法可以帮我做吗?

类似(伪代码):

1
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)

Array.Copy不适合我的需要。我需要新阵列中的项目是克隆。Array.Copy只是一个c型的memcpy等价物,这不是我要找的。


您可以将其添加为扩展方法:

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);
    }
}

但这确实要求对象是可序列化的([Serializable]ISerializable)。您可以根据需要轻松地替换任何其他序列化程序-XmlSerializerDataContractSerializer、protobuf net等。

请注意,深度克隆在没有序列化的情况下很困难;特别是,在大多数情况下,ICloneable很难被信任。


创建新数组后,可以使用Array.Copy(...)将其复制到新数组中,但我认为没有一种方法可以创建新数组并复制一系列元素。

如果使用.NET 3.5,则可以使用Linq:

1
var newArray = array.Skip(3).Take(5).ToArray();

但这样做效率会降低。

有关更具体情况的选项,请参阅类似问题的回答。


你考虑过使用ArraySegment吗?

http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx


以下代码在一行中执行此操作:

1
2
3
4
// 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();


我知道你想克隆,而不仅仅是复制参考文献。在这种情况下,可以使用.Select将数组成员投影到其克隆。例如,如果您的元素实现了IClonable,您可以这样做:

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
2
3
    object[] foo = new object[10];
    object[] bar = new object[7];  
    Array.Copy(foo, 3, bar, 0, 7);


我认为您要查找的代码是:

埃多克斯1〔6〕


在C 8中,他们引入了新的RangeIndex类型。

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
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);

号下面是我原来的帖子。它不起作用

您可以使用array.copyto:

1
2
3
4
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


这个怎么样:

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();
        }