当前位置: 首页 > article >正文

Java数组(详解版)

数组的定义:

什么是数组:

        数组:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。

1. 数组中存放的元素其类型相同
2. 数组的空间是连在一起的
3. 每个空间有自己的编号,其实位置的编号为 0 ,即数组的下标。

数组的创建及初始化:

数组的创建:

T[] 数组名 = new T[N];          

T :表示数组中存放元素的类型
T[] :表示数组的类型
N :表示数组的长度
例如:

int [] array1 = new int [ 10 ]; // 创建一个可以容纳 10 int 类型元素的数组
double [] array2 = new double [ 5 ]; // 创建一个可以容纳 5 double 类型元素的数组
String [] array3 = new double [ 3 ]; // 创建一个可以容纳 3 个字符串元素的数组

当然这样创建的数组,其实也顺便将其初始化了,内部将其每个位置的元素初始化为0.

 数组的初始化:

动态初始化:

在创建数组时,直接指定数组中元素的个数。
int [] array = new int [ 10 ];

 

此时默认将10个元素初始化为0。

具体不同类型的数组默认初始化的默认值是有区别的:

如果数组中存储元素类型为引用类型,默认值为null

静态初始化:

在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定。
T[] 数组名称 = {data1, data2, data3, ..., datan};

例如:

int [] array1 = new int []{ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
double [] array2 = new double []{ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 };
String [] array3 = new String []{ "hell" , "Java" , "!!!" };
注意:
1、静态初始化虽然 没有指定数组的长度 ,编译器在 编译时会根据{}中元素个数来确定数组的长度。
2、静态初始化时 , {} 中数据类型必须与 [] 前数据类型一致。
3、 静态初始化可以简写,省去后面的new T[]。

简写形式:

int [] array1 = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
double [] array2 = { 1.0 , 2.0 , 3.0 , 4.0 , 5.0 };
String [] array3 = { "hell" , "Java" , "!!!" };

 *数组的使用:

 数组的访问:

        这里和C与语言一样都是通过下标访问:

int [] array = new int []{ 1 , 2 , 3 , 4 , 5 };
System . out . println ( array [ 0 ]);
System . out . println ( array [ 1 ]);
System . out . println ( array [ 2 ]);
System . out . println ( array [ 3 ]);
System . out . println ( array [ 4 ]);
// 也可以通过 [] 对数组中的元素进行修改
array [ 0 ] = 10 ;
System . out . println ( array [ 0 ]);

注意:

1. 数组是一段连续的内存空间,因此 支持随机访问,即通过下标访问快速访问数组中任意位置的元素
2. 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。

 如果遇到二的情况:

在Idea中会报:

 数组越界的信息!

数组的遍历:

普通方式:

        我们可以和C语言中一样,利用for循环进行遍历:

for(int i = 0;i< arr2.length;i++){
    System.out.print(arr2[i]+" ");
}

注意:
这里求数组的长度用到了一个Java自带的一个方法,数组名.length。 

加强版for循环遍历数组:

可以使用 for-each 遍历数组:

for(int x:arr2){
    System.out.print(x+" ");
}

//int x 表示arr2中的每一个元素可以用int 类型的基本数据类型接收。

for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错。

数组是引用类型:

内存的划分:

Java虚拟机运行时,JVM中的内存被划分为5个部分:

程序计数器 (PC Register): 只是一个很小的空间 , 保存下一条执行的指令的地址。
虚拟机栈 (JVM Stack): 与方法调用相关的一些信息, 每个方法在执行时,都会先创建一个栈帧 ,栈帧中包含 有:局部变量表 操作数栈 动态链接 返回地址 以及其他的一些信息,保存的都是与方法执行时相关的一 些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了
本地方法栈 (Native Method Stack): 本地方法栈与虚拟机栈的作用类似 . 只不过 保存的内容是 Native 方法的局 部变量 . 在有些版本的 JVM 实现中 ( 例如 HotSpot), 本地方法栈和虚拟机栈是一起的
(Heap) : JVM 所管理的最大内存区域 . 使用 new 创建的对象都是在堆上保存 ( 例如前面的 new int[]{1, 2, 3} ) 堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销
方法区 (Method Area) : 用于 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数 . 方法编译出的的字节码就是保存在这个区域

 引用类型与基本数据类型的区别:

基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
而引用数据类型创建的变量,一般称为对象的引用,其空间中 存储的是对象所在空间的地址
例如:
public static void func () {
int a = 10 ;
int b = 20 ;
int [] arr = new int []{ 1 , 2 , 3 };
}

Java的数组的开辟与C语言不同,C语言数组的开辟都是在栈中开辟,但是Java数组的开辟是

在堆区开辟,虚拟机栈存储堆区开辟数组对象的地址。

C语言中数组的开辟:

引用变量:

 通过上述的分析:

        Java中的数组是一种引用,我们可以理解为存储数组的地址的变量就称为引用变量:

例如:

public static void func () {
int [] array1 = new int [ 3 ];
array1 [ 0 ] = 10 ;
array1 [ 1 ] = 20 ;
array1 [ 2 ] = 30 ;
int [] array2 = new int []{ 1 , 2 , 3 , 4 , 5 };
array2 [ 0 ] = 100 ;
array2 [ 1 ] = 200 ;
array1 = array2;
array1 [ 2 ] = 300 ;
array1 [ 3 ] = 400 ;
array2 [ 4 ] = 500 ;
for ( int i = 0 ; i < array2 . length ; i ++ ) {
System . out . println ( array2 [ i ]);
}
}

看到加红的地方,array2直接能传给array1,这里的含义就是将array2保存的数组的数组的地址传给array1,同时能让array1也可以访问arrat2指向的数组。

画图分析:

        

对null的理解:

Java中没有大写的NULL,只有小写的null。

null Java 中表示 "空引用" , 也就是一个不指向对象的引用.  

int [] arr = null ;
System . out . println ( arr [ 0 ]);
// 执行结果
Exception in thread "main" java . lang . NullPointerException
at Test . main ( Test . java : 6 )

 数组的应用:

作为函数的参数:

        这次如果想要交换两个值,可以把两个值存在数组里面进行交换:

    public static void swap(int[] arr){
    int tmp = arr[1];
    arr[1] = arr[0];
    arr[0] = tmp;
    }
    public static void main(String[] args) {
        int[] arr = {3,4};
        swap(arr);
        for(int i = 0;i< arr.length;i++){
            System.out.print(arr[i]+" ");
        }
发现在swap 方法内部修改数组的内容 , 方法外部的数组内容也发生改变 .
因为数组是引用类型,按照引用类型来进行传递, 是可以修改其中存放的内容的

作为函数的返回值:

计算斐波那契数。

public class TestArray {
public static int[] fib(int n){
if(n <= 0){
return null;
}
int[] array = new int[n];
array[0] = array[1] = 1;
for(int i = 2; i < n; ++i){
array[i] = array[i-1] + array[i-2];
}
return array;
}
public static void main(String[] args) {
int[] array = fib(10);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
}
}

 

有关数组的方法使用:

        Java 中提供了 java.util.Arrays 包, 其中包含了一些操作数组的常用方法:

数组转字符串:

import java . util . Arrays
int [] arr = { 1 , 2 , 3 , 4 , 5 , 6 };
String newArr = Arrays . toString ( arr );
System . out . println ( newArr );
// 执行结果
[ 1 , 2 , 3 , 4 , 5 , 6 ]

模拟实现Arrary.toString().

  public static void mytoString(int[] arr) {
        System.out.print("[");
        for (int i = 0; i <arr.length ; i++) {
            System.out.print(arr[i]+"");
            if(i != arr.length-1){
                System.out.print(",");
            }
        }
        System.out.print("]");
    }

数组的拷贝:

for循环拷贝:

    public static void main(String[] args) {
        int[] arr1 = {1,2,3,4,5,6};
        int[] arr2 = new int[6];
        for(int i = 0;i<arr1.length;i++) {
            arr2[i] = arr1[i];
        }
    }

使用ArrayscopyOf方法完成数组的拷贝:

        

public static void main(String[] args) {
    int[] arr1 = {1,2,3,4,5,6};
    int[] arr2 = null;
    arr2 = Arrays.copyOf(arr1,arr1.length);
    arr1[2] = 20;//为了验证是否真正拷贝了。
    System.out.println(Arrays.toString(arr2));//为了打印方便,将其转换为字符串打印
}

拷贝某个范围:

拷贝某个范围的话,可以直接使用Arrays.copyOfrange();

public static void main(String[] args) {
    int[] arr1 = {1,2,3,4,5,6};
    int[] arr2 = null;
    //arr2 = Arrays.copyOf(arr1,2*arr1.length);
    arr2 = Arrays.copyOfRange(arr1,2,5);//注意拷贝的下标为[2,5)左闭右开
    arr1[2] = 20;
    System.out.println(Arrays.toString(arr2));
}

求数组中所有元素的平均值:

    double sum = 0;
    for(int x:arr1){
        sum+=x;
    }
    double average = (double)sum/arr1.length;
    System.out.println(average);
}

  数组的二分查找:

public static int binary(int[] arr,int k){
    int mid = arr.length/2;
    int left = 0;
    int right = arr.length-1;
   while(left<right) {
       if(k>arr[mid]) {
           left = mid+1;
       }
       else if(k == arr[mid]) {
           return mid;
       }
       else {
           right = mid-1;
       }
   }
   return -1;
}

数组的逆序:

public static void reverse(int[] arr){
        int left = 0;
        int right = arr.length-1;
        while(left<right){
            int tmp = arr[left];
            arr[left] = arr[right];
            arr[right] = tmp;
            left++;
            right--;
        }
    }

上面的数组的练习需要做自己多来练,如果在C语言阶段练习过,还是建议在Java中在次练习! 


http://www.kler.cn/a/294830.html

相关文章:

  • 冗余连接2 hard题 代随C#写法
  • 6.2 对角化矩阵(2)
  • SAP_MM_SD_PP_FICO_视频课程几乎免费送
  • 【AI大模型】ELMo模型介绍:深度理解语言模型的嵌入艺术
  • 以往运维岗本人面试真题分享
  • Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)
  • Windows .NET8 实现 远程一键部署,几秒完成发布,提高效率 - CICD
  • Rust : 从事量化的生态现状与前景
  • 漫谈设计模式 [17]:状态模式
  • 调研-libevent
  • VitePress 自定义 CSS 指南
  • docker基础命令总结
  • 流程图符号速查:快速掌握流程图绘制要点
  • Kafka【十二】消费者拉取主题分区的分配策略
  • NISP 一级 —— 考证笔记合集
  • RISC-V (十二)系统调用
  • python网络爬虫(五)——爬取天气预报
  • 风趣图解LLMs RAG的15种设计模式-第一课
  • 自然语言处理系列六十二》神经网络算法》MLP多层感知机算法
  • 【C/C++】web服务器项目开发总结【请求 | 响应 | CGI】
  • 活动系统开发之采用设计模式与非设计模式的区别-非设计模式
  • Java stream使用与执行原理
  • 通信工程学习:什么是SSB单边带调制、VSB残留边带调制、DSB抑制载波双边带调制
  • Web前端主流的框架详解
  • 基于大数据的科研热点分析与挖掘系统
  • 数学建模_数据预处理流程(全)