【问题标题】:Get a slice of an array without creating a new copy在不创建新副本的情况下获取数组的一部分
【发布时间】:2013-03-12 18:56:40
【问题描述】:

我正在尝试找到一种解决方案/解决方法,用于在不创建新副本的情况下对超大数组进行切片。这是我的问题。

假设我有一个大小为 1 亿或更多的 double/int 大型数组。我将许多表示不同事物的不同数组存储在一个非常大的数组中,以显着节省内存使用量。因此,我有一个大小为 1 亿的数组,而不是每个大小为 100 的 100 万个数组。我存储索引(开始和停止)以跟踪我的数据。

我想获得数千个大小为 100 的切片。如果我使用 Arrays.copyOfRange() 方法来获得切片,它就违背了将所有内容放在一个大数组中的目的,因为每个切片都是一个占用内存的新副本.

我有遗留代码(许多人多年来编写的超过 100 万行代码)可以处理自己的数据(较小的数组)。无法修改现有代码以使用大型数组中的索引(开始、结束)。

如果我能以某种方式返回原始数组,使得返回的数组是一个引用(或假装是),其中索引 0 是原始大数组中的某个任意索引,那就太好了。

在 C/C++ 中,我可以轻松地返回一个具有特定偏移量和长度的指针,调用代码可以使用该指针。

我在 Java 中有哪些选择?

编辑:我查看了以下类似问题,但它不包含对我的问题的答复。 How to get a sub array of array in Java, without copying data?

【问题讨论】:

  • 在 Java 中无法切片数组。
  • 我所知道的唯一可以为您提供其中一部分的结构是 TreeSetTreeMap,但我不确定它们是否适用于您的问题。
  • “我在一个非常大的数组中存储了许多代表不同事物的不同数组,以显着节省内存使用”——您认为这可以节省多少内存?
  • @SantoshTiwari - 用于存储引用的 8 个字节是一个清洗,因为无论如何您都需要为每个切片提供一个引用。当您谈论 400MB 数据(100 万个数组 x 100 个元素 x 4 个字节/元素)时,似乎 12MB(100 万个数组的 12 字节标头)并不是一个巨大的开销。如果您看到内存减少了 50%,则说明发生了其他事情。
  • 好吧,考虑到 Java 不允许像 C 那样指向任意内存块的指针,而且您无法更改此遗留代码以使用自己的数据结构,看来您唯一的选择就是购买更多内存。

标签: java arrays


【解决方案1】:

您最好的选择是将切片的索引存储在单独的结构中,例如存储这些索引的数组。

这样,您不会将 大型 数组实例化为整个数据数组的分区。

【讨论】:

    【解决方案2】:

    对于一个 int 值数组,您可以用 IntBuffer 包装。您还可以包装数组的切片。

    int[] largeArray = . . .
    
    // create a slice containing the elements 100 through 149 (50 elements):
    IntBuffer slice = IntBuffer.wrap(largeArray, 100, 50);
    

    【讨论】:

    • 切片将创建我需要的范围的新副本 (IntBuffer.get()),因此它会破坏目的。
    • @SantoshTiwari - 我认为您评论了我的答案的过时版本。如图所示包装数组的一部分时,不会复制任何数据。
    • IntBuffer.wrap() 方法将返回一个无法传递给现有代码的 IntBuffer 对象。如果我可以将返回的缓冲区包装到一个 int[] 中,那么该方法可能会奏效。谢谢。
    • 好的,有一个 array() 方法可以返回一个由原始缓冲区支持的数组。它可能会起作用。我会研究这个选项。谢谢。
    • @SantoshTiwari - array() 方法返回整个后备数组,而不是切片。您必须使用 get(int[] dest) 来检索只是切片的数组。在 Java 中,int[] 不能是另一个 int[] 的一部分的别名。
    【解决方案3】:

    您能否创建自己的对象来存储原始数组的索引、大小和引用?

    class CustomizedArray {
      int startIndex;
      int size;
      int[] originalArray;
    
      public CustomizedArray(int startIndex, int size, int[] originalArray) {
        this.startIndex = startIndex;
        this.size = size;
        this.originalArray = originalArray;
       }
    
       public int getIndex(int index) {
         int originalIndex = startIndex+index;
         if(index <0 || originalIndex >= startIndex+size) {
            throw new IndexOutOfBoundException();
         }
         return originalArray[originalIndex];
    
    
    }
    

    然后您可以将 CustomizedArray 存储在更大的结构中。

    【讨论】:

      【解决方案4】:

      如何创建一个包含对原始数组和起始索引的引用的包装类,并使用此包装的实例来访问原始数组。

      下面的代码在语法上可能不正确,但它应该给你的想法。

      public class ArraySlice(){
        private int startIndex;
        private int[] originalArray;
        //getters-setters
      
        public ArraySlice(int[] originalArray, int startIndex){
          //Initialize
        }
      
        public int get(int index){
          return originalArray[startIndex+index]
        }
      }
      

      【讨论】:

      • 我们的答案几乎是完全相同的副本 :) 同时编写的。我发现它有点有趣:)
      • 正是我的想法:)
      猜你喜欢
      • 1970-01-01
      • 2017-12-28
      • 2019-05-12
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      • 2021-01-16
      相关资源
      最近更新 更多