将数组分割成长度为X的片段

15

目前我有一个大小为 N 的数组。我试图从该数组中每次复制 X 个字节。

例如,如果该数组的大小为 10,我想要大小为 3 的数组。我将复制前三个元素,然后是接下来的三个元素和最后一个元素。

目前我正在使用以下算法:

int I = 0;
int sub = bytes.length;
int counter = 0;
for (I = 0; I < bytes.length; ++I) {
    if (I % 3 == 0 && I != 0) {
       NewArray[counter] = Arrays.copyOfRange(bytes, I - 3, I));
        sub -= 3;
        ++counter;
    }
}

NewArray[counter] = Arrays.copyOfRange(bytes, I - sub, I)); //Copy remainder.

有没有更有效或更合适的方法来实现我想要的?这个算法看起来相当糟糕 =l

有什么办法可以改进它或者至少一个提示吗?

9个回答

13

这个怎么样:

int x = 3;  // chunk size
int len = bytes.length;
int counter = 0;

for (int i = 0; i < len - x + 1; i += x)
    newArray[counter++] = Arrays.copyOfRange(bytes, i, i + x);

if (len % x != 0)
    newArray[counter] = Arrays.copyOfRange(bytes, len - len % x, len);

对于所有情况下 byte.length % 3 == 0,将生成长度为0的数组,并且会在最后一次迭代时导致 ArrayIndexOutOfBounds... 当 i+x > bytes.length 时... - rolfl
修复了长度为3的倍数的情况,但在i+x > bytes.length的2/3情况下仍会存在无效数据(根据字节数据类型可能会添加额外的(byte)0值)。 - rolfl
2
@rolfl 现在应该是真正地修复了。我需要在循环条件中加上一个 + 1 - arshajii
1
newArray[counter++] 的意思是什么? - youHaveAlsoBeenABeginner

7

这里有一个方便的方法,可以将一个byte[]转换为byte[][]数组。因此,结果是一个byte[][]

public byte[][] splitBytes(final byte[] data, final int chunkSize)
{
  final int length = data.length;
  final byte[][] dest = new byte[(length + chunkSize - 1)/chunkSize][];
  int destIndex = 0;
  int stopIndex = 0;

  for (int startIndex = 0; startIndex + chunkSize <= length; startIndex += chunkSize)
  {
    stopIndex += chunkSize;
    dest[destIndex++] = Arrays.copyOfRange(data, startIndex, stopIndex);
  }

  if (stopIndex < length)
    dest[destIndex] = Arrays.copyOfRange(data, stopIndex, length);

  return dest;
}

与之前的最佳答案相比,有以下一些优势:

  1. for循环条件使用了<=,比< ... + 1更加合理。
  2. 将停止索引放入临时字段可以减少最后一个if块中的计算次数。

(单元测试)


3

这里有几件事需要做:

首先,常见的惯例不赞成使用大写字母作为变量名的开头,请将INewArray变量分别改为'i'和'newArray'。

然后,你的代码无法正常运行,因为在第一次循环时,i-3会导致IndexOutOfBounds异常.....

最后,你没有展示如何设置newArray数组的大小。

int sublen = 3; // how many elements in each sub array.
int size = ((bytes.length - 1) / sublen) + 1; // how many newArray members we will need
byte[][] newArray = new byte[size][]; 
int to = byte.length;
int cursor = size - 1;
int from = cursor * sublen;
while (cursor >= 0) {
    newArray[cursor] = Arrays.copyOfRange(bytes, from, to);
    to = from;
    from -= sublen;
    cursor --;
}

2
这里有一个函数可以分割数组,您可以使用下面的主方法来测试它。
private static List<Integer[]> splitArray(Integer[] originalArray, int chunkSize) {
List<Integer[]> listOfArrays = new ArrayList<Integer[]>();
int totalSize = originalArray.length;
if(totalSize < chunkSize ){
   chunkSize = totalSize;
}
int from = 0;
int to = chunkSize;

while(from < totalSize){
    Integer[] partArray = Arrays.copyOfRange(originalArray, from, to);
    listOfArrays.add(partArray);

    from+= chunkSize;
    to = from + chunkSize;
    if(to>totalSize){
        to = totalSize;
    }
}
return listOfArrays;
}

测试方法:

public static void main(String[] args) {
List<Integer> testingOriginalList = new ArrayList<Integer>();

for(int i=0;i<200;i++){
    testingOriginalList.add(i);
}

int batchSize = 51;
Integer[] originalArray = testingOriginalList.toArray(new Integer[]{});

List<Integer[]> listOfArrays = splitArray(originalArray, batchSize);


for(Integer[] array : listOfArrays){
    System.out.print(array.length + ", ");
    System.out.println(Arrays.toString(array));
}
}

2

这是我对此的实现,它将把您的数组拆分为最多由您决定大小的子数组,并将这些子数组放入一个数组列表中。如果数组的大小不是所选最大大小的倍数,则最后一个数组会更小。

import java.util.Arrays;
...

public static <T> List<T[]> splitArray(T[] items, int maxSubArraySize) {
  List<T[]> result = new ArrayList<T[]>();
  if (items ==null || items.length == 0) {
      return result;
  }

  int from = 0;
  int to = 0;
  int slicedItems = 0;
  while (slicedItems < items.length) {
      to = from + Math.min(maxSubArraySize, items.length - to);
      T[] slice = Arrays.copyOfRange(items, from, to);
      result.add(slice);
      slicedItems += slice.length;
      from = to;
  }
  return result;
}

1

我知道这个问题很旧,但是嘿,有人可能会搜索另一个干净的Java答案来回答这个常见的问题。 如果你正在使用List(Java 7),那么有一种非常简单和干净的方法可以获取列表的一部分:List.subList(fromIndex, toIndex)

它很容易使用。如果我以问题示例为例,那么就像这样:

int chunkSize = 3;
int counter = 0;
// bytes must be a List like an ArrayList
List<Byte> byteList = Arrays.asList(bytes);
int length = byteList.size(); 
for (int fromIndex = 0; fromIndex < length; fromIndex += chunkSize) {
   int toIndex = fromIndex + chunkSize;
   if(toIndex > length){
      toIndex = length;
   }
   NewArray[counter] = byteList.subList(fromIndex, toIndex);
   counter++;
}
// Now NewArray[] contain sub array and the last one is of the remaining length

为了摆脱“counter”,有些人可能会改变构建NewArray的方式,采用类似于List的方法,例如:
// NewArray must be a List<List<Byte>>
NewArray.addAll(byteList.subList(fromIndex, toIndex));

希望这能帮助未来的某个人!

0

您可以使用带有特殊正则表达式的split函数:

 System.out.println(Arrays.toString(
     "Thisismystringiwanttosplitintogroupswith4chareach".split("(?<=\\G.{4})")
 ));

感谢 Alan Moore 的 早期帖子。请访问并点赞。


1
如果您已经在使用字符串或数组很小,那么这样做是可以的。但对于大型字节数组,转换为/从字符串和正则表达式有点过度。 - Geobits

0
如果你需要非常大的块,并且不想独立修改它们的内容,可以考虑通过ByteBuffer.wrap()来重复地使用同一个初始数组,然后反复使用slice()。这将避免不必要的复制和内存浪费。

0
import java.util.Arrays;

public class Test {

    private void run() {
        try {

            byte[] cfsObjIds = "abcdefghij".getBytes();
            System.out.println(Arrays.toString(cfsObjIds));

            final int chunkSize = 4;
            System.out.println("Split by " + chunkSize + ":");
            int objQty = cfsObjIds.length;
            for (int i = 0; i < objQty; i += chunkSize) {
                int chunkUpperLimit = Math.min(objQty, i + chunkSize);
                byte[] cfsIdsChunk = Arrays.copyOfRange(cfsObjIds, i, chunkUpperLimit);

                System.out.println(Arrays.toString(cfsIdsChunk));
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        new Test().run();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接