java数组入门与进阶
JAVA数组的定义和静态初始化
数组的定义:
数组的定义与我们所使用的变量的定义非常的相似,比如:int a = 15;等号的左边叫做定义,等号的右边叫做赋值。我们现在学习的数组的定义是等号左边的部分。大体分为两种格式。
格式1:数据类型[ ] 数组名
示例1:int [ ] arr;
格式2:数据类型 数组名[ ]
示例2:int array[ ]
数据类型:
数据类型就是限制了我们数组以后能存什么类型的数据。
[ ] :表示我们现在定义的是一个数组,如果我们定义的时候没有写[ ]那就表示我们现在定义的是一个变量,现在我们加上了[ ]就表达我们现在定义的是一个数组
数组名:数组名就是一个名字而已,跟我们平时定义变量名啊都是一个意思。我们在定义的时候要注意,[ ] 与数组名 他们两个在定义的时候是没有前后顺序的,谁写在前面,谁写在后面都是可以的
数组的初始化
初始化:就是在内存当中开辟一块空间,讲我们数据,放进这块空间的过程
数组的初始化分两种方式,一种是静态的初始化,一种是动态的初始化
数组的静态初始化完整格式:数据类型[ ]数组名=new数据类型[ ]{元素1,元素2,元素3}
示例:int[ ] arr=new int[ ]{1,2,3};//前后的数据类型一定要保持一致,再写[ ]再写{ },{ }里面就写我们要存储的数据
此时我们如果想存小数的话,我们把数据类型变一下就可以了
示例: double arr[ ] = new double[ ]{1.1,1.2,1.3 };//数据类型改成双精度浮点型,{ }里面就写我们存储的小数
这样的完整格式,有点太过于繁琐,我们在实际的开发过程当中都是采用的简写,把new 数据类型[ ]这个地方直接省略,也就是说等号的右边我们直接写{ }
简化格式:数据类型[ ] 数组名 = {元素1,元素2,元素3........};
示例:int[ ] arr={1,2,3};
示例:double[ ] arr={1.1,2.1,3.1};
我们来看一下数组初始化后,在内存中是怎样存储的,我们以int[ ] arr={1,2,3};为例
开辟了一块空间,这一块空间我们可以把它分为三个小格子,每个小格子我们都称它为一个元素,第一个装1,第二个装2,第三个装3。注意两个点,数组的长度是3,数组一旦创建完成之后它的长度就固定了
public class JavaText {
public static void main(String[] args) {
//静态初始化
//完整格式
//数据类型[ ] 数组名 =new 数据类型[ ]{元素,元素,元素};
//简化格式
//数据类型[ ] 数组名 = { 元素,元素,元素};
//需求1: 定义数组存储5只老虎的年龄
int[] arr1=new int[]{12,13,14};//完整格式
int[] arr2={12,13,14};
//需求2:定义数组存储3个老虎的姓名
String[] arr3= new String[]{"xiaohu","xiaowang","xiaomao"};
String[] arr4 = { "xiaohu","xiaowang","xiaomao"};
//需求3定义数组存储四只老虎的身高
double[] arr5=new double[]{1.1,1.2,1.3};
double[] arr6={1.1,1.2,1.3};
}
}
数组的地址值
假设说我们定义了一个double类型数组,里面存着一些小数,这个时候我们直接用println来打印数组名,打印处理的是一个地址
double[] arr6={1.1,1.2,1.3};
System.out.println(arr6);}
运行结果:
[D@1b6d3586
//解释一下地址值的格式含义[D@1b6d3586
[ : 表示当前是一个数组
D: 表示数据类型,表示当前打印的数组是double类型的
假如说我们打印一个整型的数组,它的地址值格式含义又是什么样子的呢?示例
int[] arr2 = {12, 13, 14};
System.out.println(arr2);
运行结果:
[I@1b6d3586
//解释一下地址值的格式含义[I@1b6d3586
I:表示数组里面的元素都是int类型的,而D就是表达数组里面的类型都是double类型
@:表示一个间隔符号,也可以理解成固定格式
1b6d3586:这个东西才是数组真正的地址值(十六进制)
//我们平时习惯性的会把这个整体叫做地址值(实际上只有后面的16进制是地址值)
数组元素访问
格式:数组名[索引];
索引:假设我们现在有个长度为6的数组,分别存了a,b,c,d,e五个字母,如果我们现在要获取第一个字母,我们是不是说拿第几个就可以了?其实计算机也是一样的它在数第几个的时候,不会从1开始,而是从0开始数,这0 1 2 3 4就是索引,在数组当中索引还又另一个名字,就是下标或者角标,它就是代表我们数组中每个元素的编号
索引的特点:从0开始,逐个+1增长,连续不间断 。我们可以通过索引把数组中的元素给获取出来,也可以通过索引,把数据存储到数组当中
获取数组中第一个元素 第一个元素其实就是0索引上对应的元素(代码示例)
public class JavaText {
public static void main(String[] args) {
//利用索引对数组中的元素进行访问
//1.获取数组里面的元素
// 格式: 数组名[索引]
int[] arr={1,2,3,4,5};
//获取数组中第一个元素 第一个元素其实就是0索引上对应的元素
int a=arr[0];
}
}
运行结果:
1
如果说我们不想赋值再打印,也可以直接把他写在输出语句当中
public class JavaText {
public static void main(String[] args) {
//利用索引对数组中的元素进行访问
//1.获取数组里面的元素
// 格式: 数组名[索引]
int[] arr={1,2,3,4,5};
//获取数组中1索引上对应的数据,并直接打印出来
System.out.println(arr[1]);
}
}
运行结果:
2
把数据存储到数组当中
格式: 数组名[索引] = 具体数据/变量;
它就是表示我用具体数据或者变量,把对应数组名[索引]所存放的值覆盖掉,而原来的那个数据就会被覆盖掉,我们用代码写一下
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5};
arr[0]=25;
System.out.println(arr[0]);
}
}
结果运行:(一旦赋值之后,原来的数据就不存在了)
25
数组的遍历
数组的遍历其实非常的简单,遍历呢就是把数组中所有的内容都给取出来,取出来之后我们可以打印,可以求和,可以判断
注意:遍历是指取出数组的整个过程,不要局限的理解为遍历就是打印
我们获取数组中所有的元素,并打印出来,我们可以用循环来进行遍历
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5};
//利用循环遍历代码
//开始条件:0
//结束条件:数组长度-1(最大索引)
for(int i =0;i<=5-1;i++)
{
//i的变化范围就是0 1 2 3 4
System.out.println(arr[i]);
}
}
}
运行结果:
1
2
3
4
5
但是这个代码并不是咱们最终的代码,假设我们数组的长度是很大的一个数组,我们也不可能知道这个数组的长度。
在java当中,关于数组的一个长度属性,length
调用方式:数组名.length
比方说我们现在有一个长度很长的数组,我们现有用length来调用,并打印一下长度
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
System.out.println(arr.length);
}
}
运行结果:
20
arr数组的长度就是20,接下来我们就用length来把这个数组遍历出来
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
for(int i=0;i<=arr.length-1;i++)
{
System.out.println(arr[i]);
}
}
}
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
自动的快速生产数组的遍历方式,idea自带:数组名.fori 它会自动的生成
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
for (int i = 0; i < arr.length; i++) {
}
}
}
练习题:定义一个数组,给里面添加数据,并把数组中的元素之和打印
public class JavaText {
public static void main(String[] args) {
int[] arr={10,11,12,13,15};
int sum =0;
for (int i = 0; i < arr.length; i++) {
sum =sum+arr[i];
}
System.out.println(sum);
}
}
运行结果:
61
练习题:遍历数组得到每一个元素,统计数组里面一共有多少个能被3整除的数字
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,9,10,11,12,13,14};
int count =0;
for (int i = 0; i < arr.length; i++) {
if(arr[i]%3==0){
count++;
}
}
System.out.println("数组中可以被3整除的数有"+count+"个");
}
}
运行结果:
数组中可以被3整除的数有4个
遍历数组得到每一个元素
要求:
1,如果是奇数,则将当前数字扩大两倍
2,如果是偶数,则将当前数字变成二分之一
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7,8,9,10};
for (int i = 0; i < arr.length; i++) {
if(arr[i]%2==0){
arr[i]=arr[i]/2;
}
else {
arr[i]=arr[i]*2;
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
运行结果:
2
1
6
2
10
3
14
4
18
5
数组的动态初始化和常见问题
为什么有数组动态初始化呢?如果说数组里面要添加的数据在一开始的时候我不知道,那你说数组的{ }里面写什么呢?静态初始化跟动态初始化他们的应用场景是不一样的。
数组动态初始化
动态初始化:是手动指出数组的长度,由系统给出默认的初始化值
格式:数据类型[ ] 数组名 =new 数据类型[数组长度];
示例:int[ ] arr= new int arr[3];
这个就表示呢数组里面的元素为3,里面呢只能存放3个元素,而且存放的都是int类型的元素
练习题:定义一个数组,用来存放班级中50个学生的姓名,姓名未知,等学生报道之后,再进行添加
public class JavaText {
public static void main(String[] args) {
//数据类型[ ] 数组名 = new 数据类型[数组的长度]
//在创建的时候,由我们自己指定数组的长度,由虚拟机给出默认的初始化值
String[] arr = new String[50];
//添加学生
arr[0] = "xiaoming";
arr[1] = "xiaomao";
//获取
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//数组默认初始化的规律
//整数类型:默认初始化值0
//小数类型:默认初始化值0.0
//字符类型:默认初始化值'/u0000‘空格
//布尔类型:默认初始化值 false
//引用数据类型:默认初始化值 null
}
}
代码运行
xiaoming
xiaomao
null
数组动态初始化和静态初始化的区别
动态初始化:手动指定数组的长度,由系统给出默认初始化值
举例:使用数组容器来存储键盘录入的6个整数,因为键盘输入的数据是录入的,这个时候我们还不确定是哪几个数,所以这个时候我们就要用动态初始化。如果说我们创建一个长度为6的数组,我们可以把长度为6的数组先创建出来,等后面我们再一个一个的添加进去元素
int[] arr1=new int[6];
静态初始化:手动指定数组里面所有的元素,不需要给出它的长度,系统回根据你写的元素的个数,自动计算出数组的长度
举例:比方说我们班的同学年龄知道了,分别是18岁,19岁,20岁,那此时我们就可以用静态初始化,所有的元素全部都写在大括号当中,就可以
int[] arr6={2,3,4,5,6,7};
数组常见问题:
1.索引越界问题,就是访问了数组当中不存在的元素,发生了动态报错我们又称索引越界异常
public class JavaText {
public static void main(String[] args) {
int[] arr={1,2,3,4,5};
//长度:5
//最小索引:0
//最大索引:4 计算公式(数组的长度 - 1)
System.out.println(arr[10]);
}
}
运行结果:(报错了,发生了数组越界)
我们在以后的操作中,避免发生数组索引越界问题就行
java当中的内存划分是什么样子的 ?
java里面的内存划分也是,JVM当中的内存划分,它分为五个区域,分别为java虚拟机栈,本地方法栈,堆,方法区,程序计数器。我们常用的局部变量在栈里面,指的是java虚拟机栈。
int[] arr={1,2,3,4};
arr是局部变量,它在栈上( java虚拟机栈)它存储的是我们堆上的地址,堆上存的是1,2,3,4
此时此刻我们叫arr叫做引用变量,也叫做引用,引用是指向对象的,而我们的这个对象是在堆上的。
for-each循环
格式:for( 通过数组类型定义变量: 数组名)
public class Jase {
public static void main(String[] args) {
int[] arr={1,2,3};
for(int i:arr)
{
System.out.print(i+" ");
}
}
}
运行结果:
1 2 3
for循环与for-each有什么区别?
for循环拿不到下标的地址,但是for-each可以拿到下标的地址,如果将来只是遍历程序,
数组名作为实参传递到形参的时候,它传递的地址,所以通过for循环可以依次遍历
public class Jase {
public static void main(String[] args) {
int[] arr={1,2,3,4};
print(arr);
}
public static void print(int[] arr) {
for(int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
}
}
运行结果:
1 2 3 4
循环方法Arrays.toString(数组名)
Arrays操作数组的一个工具,它里面会有很多的方法来操作数组,通过Arrays可以对你的数组进行二分查找,排序全都可以做
toString这是一个方法将数组转换为字符串进行输出,而用到的工具是Arrays,如果需要用这个工具我们还要导包, 它是一个重载
数组名作为参数传参
数组是应用变量,引用变量中存的是“地址”,引用指向的是对象
import java.lang.reflect.Array;
import java.util.Arrays;
public class JavaClass {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4};
func1(array);
System.out.println(Arrays.toString(array));
}
public static void func1(int[] array){
array=new int[]{11,22,33,44,55};
}
}
func1(arry)传递过去的是一个引用变量,引用变量在栈区开辟了一块空间,它的值是一个地址,接收arry的形参,又在堆上面开辟了一块空间,现在栈上面有两个arry一个是形参开辟的,一个是实参开辟的他们两个共同指向堆区的空间,因为数组是ret开辟的,ret开辟的空间就是在堆区上面存放的数组是1,2,3,4,而我们下面的自定义方法里面也同样是在栈区开辟了一块空间存放数组11,22,33,44,55,自定义方法中然后我们把创建好的这个数组又给到了array,它存放的还是一个地址,不过是刚刚数组的首地址存放了进去。我们修改了形参的指向。但是我们没有修改实参。当我们自定义方法执行完之后,因为我的array是一个局部变量,局部变量的内存就会被回收,也就是出栈,随着出栈我们刚刚在堆上面创建的数组也没有引用变量指向它,它也被回收,所以我们main方法中打印的就是1 2 3 4
运行结果:
[1, 2, 3, 4]
我们来看一下第二个例子
import java.lang.reflect.Array;
import java.util.Arrays;
public class JavaClass {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4};
func2(array);
System.out.println(Arrays.toString(array));
}
public static void func2(int[] array){
array[0]=99;
}
}
这个代码呢是形参在栈上面开辟了一块空间,这个空间存放的值是一个地址,这个地址呢就是我们堆上面开辟的空间数组1,2,3,4的首地址,然后我们array[0]=99其实是把堆区数组的首地址改变了,随之形参指向的这个对象,也就是1改成了99
运行结果:
[99, 2, 3, 4]
基本类型修改形参的值
基本类型修改形参的值是修改不了的,因为他们都是在栈上面开辟的不同的两块空间,分别是存储着10,20我们改变的只是形参在栈上的空间。实参上的值没有改动,那我们非要修改形参x的值我们是做不到的,java上是拿不到x的地址的。栈上变量的地址是拿不到的,所以我们传一个基本类型是改变不了形参的值,传引用类型也是不一定能改变的,要看我们形参是否是对地址的值做的改变,如果还是单独在堆开辟的空间,那么栈上面的引用变量所指向的值也会变,这个值就是我所在堆上开辟的空间首地址(数组情况下)
import java.lang.reflect.Array;
import java.util.Arrays;
public class JavaClass {
public static void main(String[] args) {
int x =10;
fun(x);
System.out.println(x);
}
public static void fun(int x){
x=20;
}
}
运行结果:
10
数组练习:
1.数组转字符串
我们给定一个数组会把他以字符串的形式输出,那我们输出的结果就是1 2 3 4 5,我们在array数组里面存放着1 2 3 4.我们现在自己实现一个toString方法,
解题思路:toString方法是把这个字符转变成字符串,toString本身有一个返回值,所以如果我自己实现的话,我们的方法的返回值用字符串函数来接收,我们观察这个toString方法是以中括号开头的,数组后面跟着逗号,最后一个元素没有逗号。我们可以用拼接的形式,对我们当前的结果进行拼接
import java.util.Arrays;
public class JavaClass {
public static void main(String[] args) {
int[] array={1,2,3,4};
String ret = myToString(array);
System.out.println(ret);
}
//把一个整型数组,转化为字符串
//array是我们的数组参数
//return讲数组转变为字符串进行返回
public static String myToString(int[] array){
String ret = "[";
for (int i = 0; i < array.length ; i++) {
ret += array[i];
if(i!=array.length-1)
ret += ",";
}
ret += "]";
return ret;
}
运行结果:
[1,2,3,4]
数据拷贝
假设说我们此时有一个数组,我们要拷贝数组里面存放的元素1,2,3,4我是不是应该有一个数组?
public class JavaClass {
public static void main(String[] args) {
int[] array= {1,2,3,4};
int[] copy = new int[array.length];//创建了一样大小的数组空间,数组名叫做copy数组
for (int i = 0; i < array.length; i++) {
copy[i]=array[i];
}
System.out.println(Arrays.toString(copy));
}
}
代码运行
[1, 2, 3, 4]
我们上面讲的拷贝数组的方法有点太慢了,无法满足企业开发需要,我们要换一种高效率的方法,Arrays.copy0f 拷贝谁我们给一个数组名,第二个拷贝多长?
public class JavaClass {
public static void main(String[] args) {
int[] array = {1,2,3,4};
int[] copy=Arrays.copyOf(array,array.length);
System.out.println(Arrays.toString(copy));
}
}
代码运行:
[1, 2, 3, 4]
假设说我们拷贝数组的时候只想拷贝数组的1,2,3 ,第一个参数你要拷贝array,from:1,to:3,这样我们在这里就要拷贝一分部了
public class JavaClass {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] copy = Arrays.copyOfRange(array,1,3);
System.out.println(Arrays.toString(copy));
}
}
运行结果:
[2, 3]
native方法(本地方法栈)
public class JavaClass {
public static void main(String[] args) {
int[] array= {1,2,3,4,5};
int[] dest = new int[array.length];
System.arraycopy(array,0,dest,0,array.length);
System.out.println(Arrays.toString(dest));
}
}
代码运行
[1, 2, 3, 4, 5]
拷贝错误示范之“赋值”
public class JavaClass {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
int[] copy = array;
System.out.println(Arrays.toString(copy));
}
}
运行结果:
[1, 2, 3, 4, 5]
查找数组中指定的元素
Arrays.sort(数组名)给数组排好序
public class JavaClass {
public static void main(String[] args) {
int[] array ={1,2,31,14,5};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
}
}
运行结果:
[1, 2, 5, 14, 31]
二分查找
Arrays.sort(array);冒泡排序工具
二分查找一定建立在有序的情况,如果这个地方不是有序的那我们就不可以用
public class JavaClass {
public static void main(String[] args) {
int[] array ={1,2,31,14,5};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
System.out.println(erfenchazhao(array,5));
}
public static int erfenchazhao(int[] array,int key){
int i =0;
int j =array.length -1;
while (i<=j){
int mid = (i+j)/2;
if(array[mid]<key){
i =mid+1;
}
else if (array[mid]==key)
{
return mid;
}
else {
j=mid-1;
}
}
return -1;
}
}
代码运行:
[1, 2, 5, 14, 31]
2
二分查找工具Arrays.binarySearch(array,5)
public class JavaClass {
public static void main(String[] args) {
int[] array ={1,2,31,14,5};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
System.out.println(Arrays.binarySearch(array,5));
}
}
运行结果:
[1, 2, 5, 14, 31]
2
数组倒叙
public class JavaClass {
public static void main(String[] args) {
int[] array ={1,2,31,14,5};
reverse(array);
System.out.println(Arrays.toString(array));
}
public static void reverse(int[] array){
int i= 0;
int j = array.length-1;
while(i<j){
int tmp =array[i];
array[i] =array[j];
array[j] = tmp;
i++;
j--;
}
}
}
代码运行:
[5, 14, 31, 2, 1]
二维数组
二维数组本质上也是一个一维数组,只不过每个元素又是一个一维数组。
如何自己定义个二维数组?
如果我们想定义一个两行三列的数组,我们在定义二维数组的时候,必须指明每一行每一列分别是什么,以{ }的方式来指明
int[][] arr={{1,2,3},{4,5,6}};
int[][] arr2 = new int[][]{{1,2,3},{4,5,6}};
int[][] arr3=new int[2][3];//两行三列
遍历数组第一种方式for循环
public class JavaClass {
public static void main(String[] args) {
int[][] arr={{1,2,3},{4,5,6}};
for(int i = 0;i<2;i++){
for (int j=0;j<3;j++){
System.out.print(arr[i][j]+" ");
}
}
}
}
二维数组是一个特殊的一维数组
他们的每一个元素就是一个一维数组 ,0下标是一个一位数,1下标也是一个一维数组
public class JavaClass {
public static void main(String[] args) {
int[][] arr={{1,2,3},{4,5,6}};
System.out.println(arr.length);
System.out.println(arr[0].length);
System.out.println(arr[1].length);
}
}
代码运行:
2
3
3
二维数组for-each循环
格式:外层for( 一维数组: 数组名)内存for(变量:数组名)
public class JavaClass {
public static void main(String[] args) {
int[][] arr={{1,2,3},{4,5,6}};
for(int[] arr1:arr){
for (int i:arr1){
System.out.println(i+" ");
}
}
}
}
运行结果:
1
2
3
4
5
6
二维数组遍历Arrays.deepToString(数组名)
public class JavaClass {
public static void main(String[] args) {
int[][] arr={{1,2,3},{4,5,6}};
String ret = Arrays.deepToString(arr);
System.out.println(ret);
}
}
运行结果:
[[1, 2, 3], [4, 5, 6]]
二维数组定义不规则初始
定义好的
public class JavaClass {
public static void main(String[] args) {
int[][] arr={{1,2,3,9,8,7,6},{4,5,6}};
String ret = Arrays.deepToString(arr);
System.out.println(ret);
}
}
运行结果:
[[1, 2, 3, 9, 8, 7, 6], [4, 5, 6]]
没有定义好的
public class JavaClass {
public static void main(String[] args) {
int[][] arr=new int[2][];
arr[0]=new int[2];
arr[1]=new int[5];
for(int i =0;i< arr.length;i++){
for (int j=0;j<arr[i].length;j++){
System.out.println(arr[i][j]+" ");
}
}
}
}
运行结果:
0
0
0
0
0
0
0