Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ms-access/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在JAVA中,在以下方法中声明、实例化和初始化数组的时间复杂性有什么不同吗?_Java_Arrays_Optimization_Data Structures_Time Complexity - Fatal编程技术网

在JAVA中,在以下方法中声明、实例化和初始化数组的时间复杂性有什么不同吗?

在JAVA中,在以下方法中声明、实例化和初始化数组的时间复杂性有什么不同吗?,java,arrays,optimization,data-structures,time-complexity,Java,Arrays,Optimization,Data Structures,Time Complexity,我正在学习一门关于数据结构和算法的在线课程。在该课程中,讲师告诉我们以下方法的时间复杂性是不同的 方法1: Declare: int arr[]------------>O(1) Instantiation: arr = new int[size]------>O(1) Initialization: arr[0]=0;------------>O(1)

我正在学习一门关于数据结构和算法的在线课程。在该课程中,讲师告诉我们以下方法的时间复杂性是不同的

方法1:

 Declare:

                int arr[]------------>O(1)

 Instantiation:

                arr = new int[size]------>O(1)


 Initialization:
               arr[0]=0;------------>O(1)
                                     -------------->O(n)
               arr[1]=1;------------>O(1)
 Declaration,instantiation and initialization:

               int arr[]={10,20,30}---------------->O(1)
方法2:

 Declare:

                int arr[]------------>O(1)

 Instantiation:

                arr = new int[size]------>O(1)


 Initialization:
               arr[0]=0;------------>O(1)
                                     -------------->O(n)
               arr[1]=1;------------>O(1)
 Declaration,instantiation and initialization:

               int arr[]={10,20,30}---------------->O(1)
我需要知道,通过遵循第二种方法,我们可以优化我们的程序,并且如何判断它是否有
O(1)
,这两种方法之间的区别是什么

我的意思是,虽然第二种方法的步骤较少,但它内部遵循第一种方法中的所有步骤,因此它不可能是
O(1)
,它也是
O(n)
,我更正了吗?

有可能。我不知道哪种JVM实现可以做到这一点,但这是可能的。由于这项技术需要3倍的内存,任何JVM实现都很可能不使用它

假设JVM不使用这种技术,那么

arr=newint[size]

int-arr[]={10,20,30}
两者都将以O(n)运行(在第一种情况下,数组中的所有元素都需要归零)

但如果使用了这种(或类似的技术),您的讲师是正确的

编辑

如果没有奇特的初始化技术,两种情况下的复杂度都是O(n)。但常数因子可能不同。所以我使用了100个大小的数组(对不起,我懒得写更长的数组)。结果如下:

# Run complete. Total time: 00:20:13

Benchmark                                  Mode  Cnt         Score       Error  Units
ArrayInitBenchmark.initialize_and_set     thrpt  200  32341391,483 ± 46429,821  ops/s
ArrayInitBenchmark.initialize_only        thrpt  200  32523162,079 ± 34682,391  ops/s
ArrayInitBenchmark.initialize_with_zeros  thrpt  200  36267571,539 ± 34839,701  ops/s
  • 初始化和设置是OP的第一种方法
  • initialize_only
    是OP的第二种方法
  • initialize_with_zero
    仅仅是一个
    newint[size]

如您所见,第二种方法稍微快一点,但比第一种方法快不到1%(32.3 M ops/s vs 32.5 M ops/s)。也就是说,在我的机器上,使用oracle JDK 1.8.0_201。(使用Java 13进行测试需要我设置一个新的jmh项目,而我已经为Java 8设置了所有内容)。

Java的数组初始值设定项语法只是分配数组和为每个元素分配值的指令的语法糖

这可以通过以下代码轻松验证:

import java.io.IOException;
import java.nio.file.Paths;

public class ArrayInitializer {
    public void form1() {
        int arr[] = new int[3];
        arr[0] = 10;
        arr[1] = 20;
        arr[2] = 30;
    }

    public void form2() {
      int arr[] = { 10, 20, 30 };
    }

    public static void main(String[] args) throws Exception {
        decompile();
    }

    private static void decompile() throws InterruptedException, IOException
    {
      new ProcessBuilder(Paths.get(System.getProperty("java.home"))
          .getParent().resolve(Paths.get("bin", "javap")).normalize().toString(),
          "-c", "-cp", System.getProperty("java.class.path"),
          ArrayInitializer.class.getName())
      .inheritIO()
      .start().waitFor();
    }

    private ArrayInitializer() {}
}
哪张照片

从“ArrayInitializer.java”编译而来
公共类数组初始化器{
公共表格1();
代码:
0:iconst_3
1:newarray int
3:astore_1
4:aload_1
5:iconst_0
6:bipush 10
8:iStore
9:aload_1
10:iconst_1
11:bipush 20
13:iStore
14:aload_1
15:iconst_2
16:bipush 30
18:iStore
19:返回
公共表格2();
代码:
0:iconst_3
1:newarray int
3:dup
4:iconst_0
5:bipush 10
7:iStore
8:dup
9:iconst_1
10:bipush 20
12:iStore
13:dup
14:iconst_2
15:bipush 30
17:iStore
18:astore_1
19:返回
publicstaticvoidmain(java.lang.String[])抛出java.lang.Exception;
代码:
0:invokestatic#22//方法反编译:()V
3:返回
}
因此,如果编译后的代码没有差异,那么就不会有源代码形式导致的性能差异

这并不排除JVM进行的运行时优化,例如,当它检测到这是一个分配,然后填充其计算不能以泄漏未初始化数组的方式失败的值时。但这些优化将适用于任何一种情况,因为源代码形式并不重要


关于集合的
toArray
方法,在中讨论了一个实际示例,其性能取决于JVM识别数组分配的能力,该分配之后紧接着是覆盖整个创建的数组的复制操作。

它实际上遵循了所有相同的步骤吗?根据代码的构造方式,你能想出任何不同的方法吗?
newint[size]
真的是一个常数时间操作吗?可能在C中,新数组的内容未定义,但在Java中,内存必须初始化为所有
0
,这将需要一些与数组长度成线性关系的努力。@Thilo FWIW,它取决于实现,因此,Java对于0-initialized alloc必须有线性时间,这并不完全正确。编辑(以及评论中的讨论)正好指出了这一点。@vaxquis:我相信你混淆了两件事:是的,许多操作系统会给你0初始化的内存(主要是出于安全原因),但这不会是O(1),因为操作系统需要进行调零。JVM可以延迟初始化备份阵列(但这只是将O(n)延迟到以后)。从理论上讲,特殊的硬件可以在恒定的时间内将整个区域归零,或者使用类似于Mill CPU内存模型的东西,它也可以分配归零的内存,但这些都不是在任何地方实际使用的。因此,“依赖于实现”的答案太简单了。@Thilo根据场景的不同,JVM能够消除默认值的填充,例如,当后续操作保证在所有实际场景中都看不到这些值时,这意味着后续的操作是用实际值填充的,因为这是一个O(n)操作本身,所以无论分配是O(1)还是O(n),最终结果总是O(n)。这解决了初始化问题,这不是第一个代码段中唯一发生的事情。无论是否使用初始化优化,都有初始化值和设置值,而在第二个示例中,可以使用给定值初始化数组,而不是使用那些值