Java入门(四)之 数组的定义与使用
- 数组基本用法
- 什么是数组
- 创建数组
- 数组的使用
- 数组作为方法的参数
- 基本用法
- 理解引用类型
- 认识 null
- 初识 JVM 内存区域划分
- 数组作为方法的返回值
- 数组练习
- 数组转字符串
- 数组拷贝
- 找数组中的最大元素
- 求数组中元素的平均值
- 查找数组中指定元素(顺序查找)
- 查找数组中指定元素(二分查找)
- 检查数组的有序性
- 数组排序(冒泡排序)
- 数组逆序
- 数组数字排列
- 二维数组
数组基本用法
什么是数组
数组:一块连续的内存,且存储的是相同数据类型的集合。
数组本质上就是让我们能 “批量” 创建相同类型的变量。
例如:
- 如果需要表示两个数据, 那么直接创建两个变量即可 int a;int b。
- 如果需要表示五个数据, 那么可以创建五个变量 int a1; int a2; int a3; int a4; int a5;。
- 但是如果需要表示未知个数据,那么就不能创建未知个变量了。这时候就需要使用数组,帮我们批量创建。
注意事项:在 Java 中,数组中包含的变量必须是相同类型的。
创建数组
基本语法
// 动态初始化
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
// 静态初始化
数据类型[] 数组名称 = { 初始化数据 };
代码示例如下
public class TextDemo {
public static void main(String[] args) {
int[] array1 = {1,2,3,4,5,6};//1.定义且初始化一个数组(静态初始化)。
int[] array2 = new int[10];//2.定义了一个数组,但是没有初始化。new 关键字不可缺。
int[] array3 = new int[]{1,2,3,4,5,6,7,8,9};//3.动态初始化数组。
}
}
这个代码中,array1,array2,array3 都是局部变量;而{1,2,3,4,5,6},new int[10],{1,2,3,4,5,6,7,8,9}都是存放在堆区的。
注意: 数组,实际上是数组对象
引用不一定在栈上,取决于变量的位置。当变量做局部变量时,引用才在栈上。
所谓的 “引用” 本质上只是存了一个地址,Java 将数组设定成引用类型,这样的话后续进行数组参数传参,其实只是将数组的地址传入到函数形参中,这样可以避免对整个数组的拷贝(当数组可能比较长时, 拷贝开销就会很大)。
数组的使用
使用数组做局部变量时,必须对其初始化;若不知道引用对象,应使用引用对应的零值(null)。
我们来打印数组作为示例
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
int[] array2 = null;//array2 这个引用,不指向任何对象。
//此时,我们来打印创建好的数组array。
//3种方法
//1.for 循环 length:数组的属性
for (int i = 0; i < array.length;i++){
System.out.print(array[i]+ " ");
}
System.out.println();
//2.增强型for each循环
for (int x :array) {
System.out.print(x+ " ");
}
System.out.println();
//3.使用操作数组工具类。
//Arrays.toString 将数组以字符串的形式进行输出。
/*String ret = Arrays.toString(array);
System.out.println(ret);*/
System.out.println(Arrays.toString(array));
}
}
运行结果如下
这是三种打印数组的方式。
其中for()循环与for each () 循环的区别,for 循环通过下标 [ i ] 进行遍历整个数组;而for each () 循环遍历数组是不需要通过下标的。
注意事项
- 使用 array.length 能够获取到数组的长度。这个操作为成员访问操作符,在后期在面向对象中会经常用到。
- 使用 [ ] 按下标取数组元素。需要注意,下标从 0 开始计数。
- 使用 [ ] 操作既能读取数据,也能修改数据。
- 下标访问操作不能超出有效范围 [0, length - 1], 如果超出有效范围, 会出现下标越界异常。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println(array[100]);
}
运行结果如下
这个异常的原因是数组下标超出范围(数组越界访问了)。
数组作为方法的参数
基本用法
我们还是用打印数组内容作为示例
public class TextDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
printArray(arr);
}
public static void printArray(int[] a) {
for (int x : a) {
System.out.println(x);
}
}
}
运行结果如下
在这个代码中:
- int[] a 是函数的形参, int[] arr 是函数实参。
- 如果需要获取到数组长度, 同样可以使用 a.length
理解引用类型
一.参数传内置类型,代码如下:
public class TextDemo {
public static void main(String[] args) {
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x) {
x = 10;
System.out.println("x = " + x);
}
}
执行结果
可以看出,修改形参 x 的值,不影响实参的 num 值。
二.参数传数组类型,代码如下:
public class TextDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
func(arr);
System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a) {
a[0] = 10;
System.out.println("a[0] = " + a[0]);
}
}
执行结果
可以看出,在函数内部修改数组内容,函数外部也发生改变。
此时数组名 arr 是一个 “引用” 。当传参的时候,是按照引用传参的。
认识 null
null 在 Java 中表示 “空引用” ,也就是一个无效的引用。
public class TextDemo {
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr[0]);
}
}
执行结果如下
这是因为,null 的作用类似于 C 语言中的 NULL (空指针),都是表示一个无效的内存位置。因此不能对这个内存进行任何读写操作。一旦尝试读写,就会抛出 NullPointerException异常。
初识 JVM 内存区域划分
JVM Stack 虚拟机栈 :就是我们平时所说的栈,用来存储局部变量。
Native Method Stack 本地方法栈:C/C++代码。
Heap 堆:new 创建的对象都是在堆上保存,用来存放对象。
PC Register 程序计数器:只是一个很小的空间,用来保存下一条执行的指令的地址。
Method Area 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法编译出的的字节码就是保存在这个区域。
Runtime Constant Pool 方法运行池:存放字面量(字符串常量)与符号引用。(从 JDK1.7 开始, 运行时常量池在堆上)
数组作为方法的返回值
将原来数组的值,扩大二倍。改变原数组。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println("数组扩大二倍前打印");
System.out.println(Arrays.toString(array));
int[] ret = fun(array);
System.out.println("数组扩大二倍后打印");
System.out.println(Arrays.toString(ret));
System.out.println(Arrays.toString(array));
}
public static int[] fun(int[] array){
for (int i = 0;i < array.length;i++){
array[i] = array[i] * 2;
}
return array;
}
}
运行结果如下
可见,修改了原数组。
将原来数组的值,扩大二倍。不改变原数组。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
System.out.println("数组扩大二倍前打印");
System.out.println(Arrays.toString(array));
int[] ret = fun(array);
System.out.println("数组扩大二倍后打印新数组");
System.out.println(Arrays.toString(ret));
System.out.println("数组扩大二倍后,打印原数组");
System.out.println(Arrays.toString(array));
}
public static int[] fun(int[] array){
int[] ret = new int [array.length];
for (int i = 0;i < array.length;i++){
ret[i] = array[i] * 2;
}
return ret;
}
}
运行结果如下
可见,未修改原数组。
这两个代码的区别在于,是否改变原数组。
这就是典型的数组作为方法的返回值。
数组练习
数组转字符串
这道题,在这一期前面的内容有写过,非常之简单。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
String ret = Arrays.toString(array);
System.out.println(ret);
}
}
执行结果
Java 中提供了 java.util.Arrays 包,其中包含了一些操作数组的常用方法。
用自己的方法如下
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
//String ret = Arrays.toString(array);
System.out.println(toString(array));
}
public static String toString(int[] array){
String ret = "[";
for (int i = 0;i <array.length;i++){
ret += array[i];
if (i != array.length-1){
ret += ",";//各元素之间需要添加‘,’
}
}
ret += "]";
return ret;
}
}
执行结果
数组拷贝
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array1 = {1,2,3,4,5};
int[] array2 = new int[array1.length];
System.out.println("拷贝完成前,打印数组array1");
System.out.println(Arrays.toString(array1));
System.out.println("拷贝完成前,打印数组array2");
System.out.println(Arrays.toString(array2));
System.out.println("=========================");
for (int i = 0;i < array1.length;i++){
array2[i] = array1[i];
}//方法一
/* array2 = Arrays.copyOf(array1,array1.length);
//使用操作数组工具类
//方法二
*/
/*System.arraycopy(array1,0,array2,0,array1.length);
方法三
*/
/*array2 = array1.clone();
方法四
*/
System.out.println("拷贝完成后,打印数组array1");
System.out.println(Arrays.toString(array1));
System.out.println("拷贝完成后,打印数组array2");
System.out.println(Arrays.toString(array2));
//四种方法其中方法三最快。
System.out.println("=========================");
array2[0] = 99;
System.out.println("修改array2后,打印数组array1");
System.out.println(Arrays.toString(array1));
System.out.println("修改array2后,打印数组array2");
System.out.println(Arrays.toString(array2));
//拷贝后修改array2数组的值,array1数组不发生改变。
}
}
运行结果如下(其中四种方法的运行结果均相同)
只拷贝某个范围。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array1 = {1,2,3,4,5};
int[] array2 = new int[array1.length];
array2 = Arrays.copyOfRange(array1,2,5);
System.out.println(Arrays.toString(array2));
// 范围拷贝
}
}
执行结果
找数组中的最大元素
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] arr = {12,21,33,24,45,16};
System.out.println(max(arr));
}
public static int max(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
}
执行结果
求数组中元素的平均值
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7};
System.out.println(avg(arr));
}
public static double avg(int[] arr) {
int sum = 0;
for (int x : arr) {
sum += x;
}
return (double)sum / (double)arr.length;
}
}
执行结果
查找数组中指定元素(顺序查找)
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] arr = {21,12,3,81,59,36};
System.out.println(find(arr, 81));
}
public static int find(int[] arr, int toFind) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == toFind) {
return i;
}
}
return -1; // 表示没有找到
}
}
执行结果
查找数组中指定元素(二分查找)
针对有序数组,可以使用更高效的二分查找。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(binarySearch(arr, 6));
}
public static int binarySearch(int[] arr, int toFind) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (toFind < arr[mid]) {
// 去左侧区间找
right = mid - 1;
} else if (toFind > arr[mid]) {
// 去右侧区间找
left = mid + 1;
} else {
// 相等, 说明找到了
return mid;
}
}
// 循环结束, 说明没找到
return -1;
}
}
执行结果
检查数组的有序性
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,10,5,6};
System.out.println(isSorted(arr));
}
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
执行结果
数组排序(冒泡排序)
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {9, 9, 8, 1};
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
public static void bubbleSort(int[] arr){
for (int i = 0; i < arr.length; i++) {
for (int j = arr.length - 1; j > i; j--) {
if (arr[j - 1] > arr[j]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
}
}
执行结果
Java中,实现数组排序可以使用操作数组工具类。代码如下
public class TextDemo {
public static void main(String[] args) {
int[] arr = {3, 2, 5, 1};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
执行结果
数组逆序
给定一个数组,将里面的元素逆序排列。
思路
设定两个下标, 分别指向第一个元素和最后一个元素. 交换两个位置的元素.
然后让前一个下标自增, 后一个下标自减, 循环继续即可。
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4,5,6,7,8,9};
reverse(arr);
System.out.println(Arrays.toString(arr));
}
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--;
}
}
}
执行如下
数组数字排列
给定一个整型数组,将所有的偶数放在前半部分,将所有的奇数放在数组后半部分。代码如下:
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6,7,8};
transform(array);
System.out.println(Arrays.toString(array));
}
public static void transform(int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
// 该循环结束, left 就指向了一个奇数
while (left < right && arr[left] % 2 == 0) {
left++;
}
// 该循环结束, right 就指向了一个偶数
while (left < right && arr[right] % 2 != 0) {
right--;
}
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}
}
执行结果
二维数组
二维数组本质上也就是一维数组,只不过每个元素又是一个一维数组。
基本语法
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
我们用打印一个二维数组作为示例,代码如下:
import java.util.*;
public class TextDemo {
public static void main(String[] args) {
int[][] array = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
for (int row = 0; row < array.length; row++) {
for (int col = 0; col < array[row].length; col++) {
System.out.printf("%d\t", array[row][col]);
}
System.out.println("");
}
}
}
执行结果
二维数组的用法和一维数组并没有明显差别,因此我们不再赘述。
同理,还存在 “三维数组”,“四维数组” 等更复杂的数组, 只不过出现频率都很低,很少用到。
Java入门(四)之 数组的定义与使用,到这里就基本结束了。
下期带来Java入门(五)之 类和对象。各位铁铁,我们下期见。