【问题标题】:Inefficient java / arrays of unknown length效率低下的 java / 未知长度的数组
【发布时间】:2014-08-29 19:26:39
【问题描述】:

我正在制作一些用于查找某个数的质因数的方法。这被分解为两个都使用数组的函数。但是,在这两个函数中,代码的效率都非常低。首先我必须计算数组的长度,创建一个该长度的新数组,然后使用几乎完全相同的代码来填充数组。

有没有一种方法可以使数组的宽度未知,并在找到整数时将它们推到数组的末尾?

这是我的代码:

public class JavaApplication7{
    public static void main(String[] args) {
        System.out.println(Arrays.toString(primeFactors(85251)));
    }
    public static int[] primeFactors(int num){
        int[] factors = primesUpTo(num);
        int originalNum = num;
        int i = 0;
        int count = 0;
        while(num != 1){
            if(num % factors[i] == 0){
                num /= factors[i];
                i = 0;
                count++;
            }else{
                i++;
            }
        }
        int[] primeFactors = new int[count];
        i = 0;
        count = 0;
        while(originalNum != 1){
            if(originalNum % factors[i] == 0){
                originalNum /= factors[i];
                primeFactors[count] = factors[i];
                i = 0;
                count++;
            }else{
                i++;
            }
        }
        return primeFactors;
    }
    public static int[] primesUpTo(int upTo){
        int count = 0;
        int num = 2;
        while(num <= upTo){
            boolean isPrime = true;
            for(int div = 2; div <= num / 2; div++){
                isPrime = num % div == 0 ? false : isPrime;
            }
            count += isPrime ? 1 : 0;
            num++;
        }
        int i = 0;
        num = 2;
        int[] primes = new int[count];
        while(num <= upTo){
            boolean isPrime = true;
            for(int div = 2; div <= num / 2; div++){
                isPrime = num % div == 0 ? false : isPrime;
            }
            if(isPrime){
                primes[i] = num;
                i++;
            }
            num++;
        }
        return primes;
    }    
} 

【问题讨论】:

  • 查找ArrayList
  • 请解释谁阻止使用会根据需要增长的 ArrayList?
  • 需要注意的是,一个ArrayList默认是由一个大容量数组支持的,但是当它的容量超出时,它需要创建一个新数组并执行一个副本,就像他手动做的那样这里。没有办法解决这个问题,因为数组在内存中是连续的。其他不受数组支持的集合类型不会受到此影响。

标签: java arrays performance repeat


【解决方案1】:

您可以使用比数组更动态的Arraylists。

但是,在这两个函数中,代码的效率都非常低,因为我首先拥有 要计算数组的长度,请创建一个该长度的新数组,然后 然后使用几乎完全相同的代码来填充数组

但是,您会发现Arraylists 看起来确实是动态的,但实际上它们在做类似的事情。他们从一个大小开始,然后将底层Array 复制到更大的大小等。

如果您知道必须存储多少数字的上限,您可以做的另一件事是实现您自己的容器类。它可以有一个大数组来保存数字和一个长度变量,用于循环遍历元素。

例如:

public class NumberContainer(){

    private int[] elements;
    private int numOfElements;

    public NumberContainer(int size){
        elements = new int[size];
        numOfElements = 0;
    }

    //add a number

    public void add(int x){
        elements[numOfElements] = x;
        numOfElements++;
    }

    //get length
    public int length(){
        return numOfElements;
    }

}

....等等。

这样您就不必将Array 复制到一个新的大的,总是假设您实例化NumberContainer 具有足够大的大小。

希望对你有帮助

【讨论】:

    【解决方案2】:

    您可以使用ArrayList,它是空的,没有特定的大小,您可以随时添加 (-> add(Object o) 或删除 (-> remove(int index))。

    【讨论】:

      【解决方案3】:

      如果您仍需要通过索引快速检索,请使用ArrayList。否则考虑一个 LinkedList,因为 add = O(1)。

      对于链表

      get(int index) is O(n)
      add(E element) is O(1)
      add(int index, E element) is O(n)
      remove(int index) is O(n)
      Iterator.remove() is O(1) <--- main benefit of LinkedList<E>
      ListIterator.add(E element) is O(1) <--- main benefit of LinkedList<E>
      

      对于数组列表

      get(int index) is O(1) <--- main benefit of ArrayList<E>
      add(E element) is O(1) amortized, but O(n) worst-case since the array must be resized and copied
      add(int index, E element) is O(n - index) amortized, but O(n) worst-case (as above)
      remove(int index) is O(n - index) (i.e. removing last is O(1))
      Iterator.remove() is O(n - index)
      ListIterator.add(E element) is O(n - index)
      

      When to use LinkedList over ArrayList?

      【讨论】:

        【解决方案4】:

        我做到了

                boolean isPrime = true;
                for (int div = 2; div <= num / 2; div++) {
                    if (num % div == 0) {
                        isPrime = false;
                        break;
                    }
                    // Instead isPrime = num % div == 0 ? false : isPrime;
                }
        

        所需时间从 13 秒变为 1 秒。

        其实我也想试试

        public static int guessedPrimeCount(int upTo) {
            if (upTo < 10) {
                return 10;
            }
            return (int) (upTo / Math.log10(upTo - 1));
        }
        
        public int[] addToPrimes(int[] primes, int count, int p) {
            if (count >= primes.length) {
                primes = Arrays.copyOf(primes, count + 10);
            }
            primes[count] = p;
            return primes;
        }
        
        primes = addToPrimes(primes, count, num);
        ++count;
        

        guessedPrimeCount 已记录在案,x/log x 或 x/log(x-1)。 在 [count] 处添加新的素数 p 时,在最坏的情况下必须复制整个数组。

        【讨论】:

          【解决方案5】:

          你可以使用

          ArrayList<Integer>
          

          但由于自动装箱,这需要大量内存开销。

          或者您可以使用优秀的 GNU Trove3 库。这些包含一个TIntArrayList,它会为您调整大小;并且本质上是一个int[] + 一个长度字段。追加到then的逻辑大致是:

          double[] array = new double[10]; // Allocated space
          int size = 0; // Used space
          
          void add(int v) {
              if (size == array.length) {
                  array = Arrays.copyOf(array, array.length * 2);
              }
              array[size++] = v;
          }
          

          【讨论】:

          • 不要自动装箱,只制作整数。 (自动装箱的优化在 1.6 中得到了极大的改进)
          • 不幸的是,自动装箱没有被Collections优化掉了。因为这些也可能包含null。使用原始数组或优化集合真的很划算。
          • 参见例如此内存性能基准:takipiblog.com/…
          猜你喜欢
          • 2010-10-14
          • 2014-05-04
          • 1970-01-01
          • 2019-12-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多