数组(Array)
数组分为一维数组和二维数组。
1、一维数组
1.定义
存储一组同一个类型数据的容器叫做数组。
数组对每一个存入的数字都会自动编号,编号是从0开始的,这个编号叫下标。
2.格式:
1.数据类型[] 数组名 = new 数据类型[表示数组能存放的元素个数];
适用于一些知道元素个数而不知道具体元素的场景。
例如:
int[] arr = new int[5];
//表示定义了一个能存储5个int类型的数据的数组。
int arr[];//允许先声明,再初始化。
arr = new int[3];
int[] arr1 = new int[5];
arr[2] = 15; int i = arr[2];
System.out.println(arr[2]);
2.数据类型[] 数组名 = new 数据类型[]{元素1,元素2,……,元素n};
这种形式同时也就规定了数组的大小就是元素的个数。
例如:
int[] arr = new int[]{3,5,1,2,5};
int[] arr1;//允许先声明再初始化。
arr1 = new int[]{3,5,1,2,4,7}
arr[3] = 10;
注意:数组一旦定义好,大小不可变。
3.数据类型[] 数组名 = {元素1, 元素2,……,元素n};
例如:
int[] arr = {2,6,4,7,9};
3.内存
这里先简单说一下内存的一些东西。Java将内存分为了五块:栈内存,堆内存,方法区,本地方法栈,寄存器。
注意:内存的初始化过程在程序之前,内存在初始化完成之后才会赋值。
对于基本类型而言,传值传的是实际值,对于引用类型而言传值传的是地址。
栈内存
存储变量,执行代码块的。
①变量是存储在栈内存中的。
②变量在栈内存中用完之后立即移除,释放内存空间,提高内存的利用率。
③变量在栈内存中不会自动赋予默认值。
堆内存
存储对象。对象在堆内存中会自动的赋予一个默认值。对象在使用完成之后,不会立即移除,而是在不定的某个时刻被回收。
各个数据类型在堆内存中的默认值:
byte/short/int为0,long为0L,float为0.0f,double为0.0,char为‘\u0000’,boolean为false,所有的引用类型的默认值全部都是null。
在操作数组的过程中,经常会遇到下列异常:
ArrayIndexOutBoundsException :数组下标越界异常。
编译没有问题,说明语法没有问题,逻辑上或者事实上不符合,才会运行报错。
NullPointerException :空指针异常。
4.数组的应用
①获取指定位置上的元素
数组名[下标]:arr[n];。
int arr[] ={0,5,6,2,7,9,1};
System.out.println(arr[3]);//输出为元素2,下标是从0开始的。
②获取数组的长度
数组名.length:arr.length;。
int arr[] = new int[5];
System.out.println(arr.length);//输出的结果是5.
③遍历数组(ergodic array)
遍历数组的方式有三种:
1.普通for循环
for(int i = 0;i < arr.length; i ++){
System.out.println(arr[i]);
}
2.增强for循环
①增强for循环是基于迭代器。
②无法改变原来数组的元素。
// 表示把数组中的元素依次取出赋值给i
for(int i : arr){
System.out.println(i);
}
for(String s : arr){
System.out.println(s);
}
3.Arrays.toString();
将数组中的元素依次取出,拼接成一个字符串。
String str = Arrays.toString(arr);
System.out.println(Arrays.toString(arr));
④获取数组元素的最值。
获取最大值用遍历数组。
方法一:增强for循环。
int max = arr[0];
for(int i : arr){
if(max < i){
max = i;
}
}
System.out.println(max);
方法二:普通for循环。
int max = 0;
for(int i = 0; i < arr.length; i++){
if(arr[i] > arr[max]){
max = i;
}
}
System.out.println(arr[max]);
⑤数组元素排序
数组元素排序有冒泡排序、选择排序、快速排序、希尔排序、堆
排序等多种排序方法。
(1)冒泡排序(掌握)
方法如下:
for(int i = 1; i <= arr.length - 1; i++){
for(int j = 1 ; j <= arr.length - i; j++){
if(arr[j - 1] > arr[j]){
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
(2)选择排序(掌握)
方法如下:
for(int i = 1;i < arr.length;i++){
for(int j = i - 1;j <arr.length; j++){
if(arr[i-1] > arr[j]){
int temp = arr[i -1];
arr[i-1] = arr[j];
arr[j] = temp;
}
}
}
(3)Arrays.sort()
需要导包语句,只能从小到大排序。
import java.util.Arrays;
public class Test{
public static void main(String[] args){
int arr[] = {5,6,8,4,1,2,7}
Arrays.sort(arr);
System.out.pirntln(arr);
}
}
⑥查找元素的位置
A.遍历
针对无序数组。
class MyClass{
public static void main(String[] args){
int[] arr={5,6,2,4,8,1,3,7};
int a = 4;
int index = -1;
for(int i=0;i < arr.length;i++){
if(arr[i]==a){
index = i;
break;
}
}
System.out.println(index);
}
}
B.折半查找(掌握)
针对有序数组。时间复杂度:O(log2n)
class MyClass{
public static void main(String[] args) {
int[] a={1,3,4,6,8,9,15,19,25,36,48};
int num = 25;
int min = 0;
int max = a.length - 1;
int mid = (min + max) / 2;
boolean b = true;
while(min <= max){
if(num == a[mid]){
System.out.println(mid);
b = false;
break;
}else if(num > a[mid]){
min = mid+1;
}else if(num < a[mid]){
max = mid-1;
}
mid = (min+max) / 2;
}
if(b)
System.out.println("no");
}
}
⑦反转数组
头尾交换数组的元素。
方法一:利用第三方数组。
方法二:利用两个变量同时操作数组的两端。
int[] arr = {12,15,26,38,49,80,91};
for(int start = 0, end = arr.length - 1; start < end ;start++,end--){
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
System.out.println(Arrays.toString(arr));
注意:如果访问的下标不存在,会出现ArrayIndexOutOfBoundsEx
ception:数组下标越界异常,编译的时候没有报错,因为语法无错,所以编译检查的是语法问题。
⑧数组的扩容/复制
实际上是产生了一个新的数组。
System.arraycopy()
System.arraycopy(要复制的数组, 要复制的起始下标, 要存放的数组, 存放的起始位置, 复制的元素的个数);
System.arraycopy(arr,3,arr1,2,4);//从arr中3的位置开始复制,存放到arr1中,从arr1中2的位置开始存放,复制4个元素。
int arr = {1,2,3,4,5};
int arr1= new int[arr.length * 2];
System.arraycopy(arr,0,arr1,0,arr.length);
arr = arr1;
Arrays.copyOf()
arr=Arrays.copyOf(要扩容的数组, 扩容之后的大小);数组在扩容完成之后,堆内存的地址已经发生了改变,底层使用的就是System.arraycopy();。
Arrays.copyOf(arr,len);//底层代码。
int[] arr = {1,2,3,4}
int[] arr2 = new int[len];
if(len < arr.length){
System.arraycopy(arr,0,arr2,0,len);
} else {
System.arraycopy(arr,0,arr2,0,arr.length);
}
arr = arr2;
2、二维数组
1.定义
二维数组的每一个元素就是一个一维数组,是一个存放数组的容器。实际上二维数组中存储的是一维数组在堆内存中一维数组的内存地址。
2.格式
1.数据类型[][] 数组名 = new 数据类型[二维数组的大小][一维数组的大小];
int[][] arr = new int[3][5];
//表示定义了一个包含了3个一维数组的二维数组,每个一维数组包含5个整数元素。
arr[1][3] = 5;//赋值
int[]arr[] = new int[3][5];
int arr[][] = new int[3][5];
int[] arr2 = arr[0];//获取的是一个一维数组。
arr[0][2]; //获取具体的元素,需要两个下标。
2.数据类型[][] 数组名 = new 数据类型[二维数组的大小][];
int[][] arr = new int[5][];//表示二维数组中包含5个一维数组。
//给每个一维数组规定长度。
arr[0] = new int[5];
arr[1] = new int[3];
arr[2] = new int[0];
arr[3][0] = 8;//NullPointerException:空指针异常
注意:针对null的任何操作都会出现空指针异常
3.数据类型[][] 数组名 = {{元素1,元素12,……},{元素21,元素22,……},……{}};
int[][] arr = {{2,3}, {1}, {3,5,6}, {1,2,3,7}, {1}};
笔试题总结:
对于数组int[] x,y[]均已初始化,下面各项正确的是:BC。 y[][] x[]
A. y[0] = x[0];
B. y[0] = x;//一维数组赋值给二维数组。
C. y[0][0] = x[0];//一维数组的具体值,赋值给二维数组的具体元素。
D. y[0][0] = x;
E. y[0] = x[0][0];
注意:[]在变量名之前紧跟的是数据类型,以后定义的每一个变量都会拥有这个[];如果[]在变量名之后,那么[]只属于当前的变量。
3.应用:
①二维数组的长度
数组名.lenght。 arr.length
每个一维数组:数组名[下标].lengyh。 arr[0].length
②遍历
遍历二维数组需要两层循环。
方法一:双层for循环。
for(int i=0;i<arr.length;i++){
for(int j = 0;j < arr[i].length; j++){
System.out.println(arr[i][j]);
}
}
方法二:Arrays.toString();
输出的是二维数组中存储的一维数组的地址。
③二维数组的反转
二维数组里面的各个一维数组头尾交换位置。
for(int statt = 0,end = arr.length - 1; start < end ; start++,end--){
int temp[] = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
练习:
杨辉三角(二维数组)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
输入一个数n,输出前n行
//从控制台获取行数
Scanner s = new Scanner(System.in);
int row = s.nextInt();
//根据行数定义好二维数组,由于每一行的元素个数不同,所以不定义每一行的个数
int[][] arr = new int[row][];
//遍历二维数组
for(int i = 0; i < row; i++){
//初始化每一行的这个一维数组
arr[i] = new int[i + 1];
//遍历这个一维数组,添加元素
for(int j = 0; j <= i; j++){
//每一列的开头和结尾元素为1,开头的时候,j=0,结尾的时候,j=i
if(j == 0 || j == i){
arr[i][j] = 1;
} else {
//每一个元素是它上一行的元素和斜对角元素之和
arr[i][j] = arr[i -1][j] + arr[i - 1][j - 1];
}
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}