Получение подматрицы из существующего массива
У меня есть массив X из 10 элементов. Я хотел бы создать новый массив, содержащий все элементы из X, которые начинаются с индекса 3 и заканчиваются индексом 7. Конечно, я могу легко написать цикл, который сделает это для меня, но я хотел бы сохранить мой код как можно более чистым, Есть ли способ на С#, который может сделать это для меня?
Что-то вроде (псевдокод):
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy
не соответствует моим потребностям. Мне нужно, чтобы элементы в новом массиве были клонами. Array.Copy
- это эквивалент C-Style memcpy
, это не то, что я ищу.
Ответы
Ответ 1
Вы можете добавить его как метод расширения:
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
). Вы можете легко заменить любой другой сериализатор по мере необходимости - XmlSerializer
, DataContractSerializer
, protobuf-net и т.д.
Обратите внимание, что глубокий клон непросто без сериализации; в частности, ICloneable
трудно доверять в большинстве случаев.
Ответ 2
Вы можете использовать Array.Copy(...)
для копирования в новый массив после его создания, но я не думаю, что есть способ который создает новый массив и копирует ряд элементов.
Если вы используете .NET 3.5, вы можете использовать LINQ:
var newArray = array.Skip(3).Take(5).ToArray();
но это будет несколько менее эффективно.
См. этот ответ на аналогичный вопрос для вариантов более конкретных ситуаций.
Ответ 3
Считаете ли вы использование ArraySegment
?
http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx
Ответ 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();
Ответ 5
Я вижу, вы хотите делать клонирование, а не просто копировать ссылки. В этом случае вы можете использовать .Select
для проецирования элементов массива на их клонов. Например, если ваши элементы реализовали IClonable
вы можете сделать что-то вроде этого:
var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
Примечание. Для этого решения требуется .NET Framework 3.5.
Ответ 6
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };
arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
Ответ 7
Вы можете сделать это довольно легко;
object[] foo = new object[10];
object[] bar = new object[7];
Array.Copy(foo, 3, bar, 0, 7);
Ответ 8
Построение ответа на Marc, но добавление желаемого поведения клонирования
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åvard Strandens Copyable library, чтобы сделать требуемый тяжелый подъем.
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 работает с любым из:
Для автоматизированной копии для работы, например, должно выполняться одно из следующих утверждений:
- Его тип должен иметь конструктор без параметров или
- Он должен быть скопирован или
- Он должен иметь зарегистрированный для него тип IInstanceProvider.
Таким образом, это должно охватывать практически любую ситуацию. Если вы клонируете объекты, в которых вспомогательный график содержит такие вещи, как соединения db или дескрипторы файлов/потоков, у вас, очевидно, есть проблемы, но это верно для любой обобщенной глубокой копии.
Если вы хотите использовать какой-то другой подход с глубокой копией, вместо этого в статье перечислены несколько других, поэтому я предлагаю не пытаться писать свои собственные.
Ответ 9
Я думаю, что код, который вы ищете, это:
Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)
Ответ 10
В качестве альтернативы копированию данных вы можете создать оболочку, которая дает вам доступ к части исходного массива, как если бы это была копия части массива. Преимущество состоит в том, что вы не получаете другую копию данных в памяти, а недостаток - небольшая служебная информация при доступе к данным.
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
Ответ 11
В С# 8 они ввели новый тип Range
и Index
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 }
Ответ 12
Array.ConstrainedCopy будет работать.
public static void ConstrainedCopy (
Array sourceArray,
int sourceIndex,
Array destinationArray,
int destinationIndex,
int length
)
Ответ 13
Нет единого метода, который будет делать то, что вы хотите. Вам нужно будет сделать метод clone доступным для класса в вашем массиве. Затем, если LINQ является опцией:
Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();
class Foo
{
public Foo Clone()
{
return (Foo)MemberwiseClone();
}
}
Ответ 14
Как насчет использования 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);
Ниже мой оригинальный пост. Он не будет работать
Вы можете использовать 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
Ответ 15
Как насчет этого:
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 для всех классов, которые вам нужны для использования, но это должно сделать это.
Ответ 16
Я не уверен, насколько это действительно так, но:
MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()
Это немного накладных расходов, но он может вырезать ненужный метод.
Ответ 17
Клонирование элементов в массиве не является чем-то, что можно сделать универсальным способом. Вы хотите глубокого клонирования или простой копии всех участников?
Отпустите для подхода "наилучшего усилия": клонирование объектов с помощью интерфейса 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);
}
}
}
Это не идеальное решение, потому что просто нет ни одного, который будет работать для любого типа объекта.
Ответ 18
Что касается клонирования, я не думаю, что сериализация вызывает ваши конструкторы. Это может нарушить инварианты класса, если вы делаете интересные вещи в ctor's.
Кажется, что более безопасная ставка - это методы виртуального клонирования, вызывающие конструкторы копирования.
protected MyDerivedClass(MyDerivedClass myClass)
{
...
}
public override MyBaseClass Clone()
{
return new MyDerivedClass(this);
}
Ответ 19
Вы можете взять класс, сделанный Microsoft:
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));
}
}
Ответ 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]);
}
}
Надеюсь, что это поможет!
Ответ 21
использовать метод расширения:
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;
}
и вы можете использовать его
var NewArray = OldArray.Slice(3,7);
Ответ 22
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();
}