Java温习——数组

定义方法的形参时,最好不超过5个;


1 概念

数组,是指按一定顺序排列的相同数据类型的集合(若干变量);

元素,是指数组中的每个数据;

数组中的元素以索引表示其存放位置,索引从0开始,步长为1;


数据类型包括基本数据类型(byte、char、short、int、long、float、double、boolean)、引用数据类型(类、接口、数组);

2 定义

<数组元素的数据类型>[] <数组名>; // 定义方式1,如int[] ages;
<数组元素的数据类型> <数组名>[]; // 定义方式2,如int ages[];


注:

推荐使用方式1进行数组定义,因为可以把<数组元素的数据类型>[]看成一个整体;


3 数组的初始化

数组必须先初始化才能使用,因为初始化表示在内存中分配空间;

数组初始化有两种方式——静态和动态初始化;

但无论以哪种方式进行初始化,一旦完成初始化,数组的长度固定,不能改变,除非重新初始化——数组是定长的;

(1)静态初始化

a 概念

由程序员为每个数组元素设置初始值,而数组的长度由系统决定;

<数组元素的数据类型>[] <数组名> = new <数组元素的数据类型>[]{元素1, 元素2, 元素3, ...}; // 如int[] ages = new int[]{17, 18, 19};
<数组元素的数据类型>[] <数组名> = {元素1, 元素2, 元素3, ...}; // 简单形式的初始化


注:

数组长度计算——<数组名>.length

简单形式的初始化必须声明后立即初始化,不能先声明后初始化;


b 内存分析



(2)动态初始化

a 概念

由程序员设置数组长度,每个数组元素的初始值由JVM决定;

int <数组元素的数据类型>[] <数组名> = new <数组元素的数据类型>[<int类型的数组长度>]; // 如int[] ages = new int[3];


b 不同数据类型的初始值



c 内存分析



注:

不能同时使用静态初始化和动态初始化,如int[] ages = new int[3]{17, 18, 19};语句是错误的;

动/静态初始化的使用情形 —— 当事先知道存储哪些数据,使用静态初始化,否则使用动态初始化;


4 数组基本操作

(1)获取元素

通过索引获取相应的元素;

<数组元素的数据类型> <变量名> = <数组名>[<索引index>];


(2)设置元素

<数组名>[<index>] = <数值>;


(3)遍历元素

使用for循环语句;

int[] ages = new int[]{1,2,3};
for(int index = 0; index < ages.length; index ++){
	System.out.println(ages[index]);
}


(4)获取数组长度

int len = <数组名>.length // length是属性,而非方法

(5)获取索引范围

[ 0, <数组名>.length-1]


5 操作数组常见异常

(1)NullPointerException

空指针异常,即空引用;

当数组还未初始化就直接对其操作,会引起该异常;


(2)ArrayIndexOutOfBoundsException

数组索引越界异常,即索引不在正确范围内;


6 数组编程

(1)获取数组最大/小元素

static int maxValue(int num[]){
	if( num == null || num.length == 0){ // 首先判断传递过来的数组是否为空,或者数组长度为0,若是则直接返回,否则才进行后续操作
		return -1;
	}
	int len = num.length;
	int max = num[0];
	for(int i = 1; i < len; i ++){
		if(max < num[i]){
			max = num[i];
		}
	}
	return max;
}

static int minValue(int num[]){
	if( num == null || num.length == 0){ 
		return -1;
	}
	int len = num.length;
	int min = num[0];
	for(int i = 1; i < len; i ++){
		if(min > num[i]){
			min = num[i];
		}
	}
	return min;
}


(2)按格式打印数组元素

static String printArray(int[] num){
	if( num == null || num.length == 0){ 
		return "[]";
	}
	int len = num.length;
	String str = "[";
	for(int i = 0; i < len; i++){
		str += num[i];
		if(i != len - 1){
			str += ", ";
		}
	}
	str += "]";
	return str;
}


(3)逆序排列数组

static void reverseArray(int[] num){
	if(num == null || num.length == 0){
		return;
	}
	int len = num.length;
	int halfLen = num.length / 2;
	int temp;
	for(int i = 0; i < halfLen; i++){
		temp = num[i];
		num[i] = num[len - 1 - i];
		num[len - 1 - i] = temp;
	}
	return;
}


(4)元素第一次/最后一次出现索引(线性搜索)

static int firstIndex(int[] arr, int num){
	if(arr == null || arr.length == 0){
		return -1;
	}
	int len = arr.length;
	for(int i = 0; i < len; i++){
		if(num == arr[i]){
			return i;
		}
	}
	return -1;
}
static int lastIndex(int[] arr, int num){
	if(arr == null || arr.length == 0){
		return -1;
	}
	int len = arr.length;
	for(int i = len - 1; i >= 0; i--){
		if(arr[i] == num){
			return i;
		}
	}
	return -1;
}


7 方法参数的值传递机制

(1)main方法的数组参数

main方法是static修饰的,可直接使用所在类的类名进行调用;

底层是JVM通过类名.main(new String[]{});运行程序的;


main方法的String数组参数,其实是暴露给程序运行者,用于给程序传递一个数据信息——任何程序交互的窗口;

args[0]表示第一个参数,以此类推;

使用Windows命令行窗口编译后,使用“java <类名> <参数列表>”命令运行程序;


如下程序,用于输出String数组参数的长度和内容

class Hello 
{
	public static void main(String[] args) 
	{
		System.out.println(args.length);
		for(int i = 0; i < args.length; i ++){
			System.out.print(args[i] + "\t");
		}
		System.out.println();
	}
}

运行结果如下:


(2)基本数据类型

基本数据类型作为参数进行传递时,采用值传递机制,传递的是基本类型的值的副本;


如下,意图改变基本数据类型的值

class ParameterDemo 
{
	static void change(int x){
		System.out.println("change方法修改前: " + x); // 10
		x = 50;
		System.out.println("change方法修改后: " + x); // 50
	}
	public static void main(String[] args) 
	{
		int x = 10;
		System.out.println("main方法修改前: " + x); // 10
		ParameterDemo.change(x); // 改变x变量
		System.out.println("main方法修改后: " + x); // 10,这里易出错
	}
}

对应的内存分配图如下:

因为数据是基本数据类型,所以分类内存空间的是栈,而非堆;

传递基本数据类型的参数,其实是将该参数的值的副本传递给调用的方法;


(3)引用数据类型

引用数据类型作为参数进行传递时,也采用值传递机制,传递的是引用的地址值的副本;

class ParameterDemo 
{
	static void printArray(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		String str = "[";
		for(int i = 0; i < arr.length; i++){
			str += arr[i];
			if(i != arr.length - 1){
				str += ", ";
			}
		}
		str += "]";
		System.out.println(str);
	}
	static void swap(int[] arr){
		System.out.print("change方法修改前: "); // [10, 99]
		ParameterDemo.printArray(arr);
		int temp;
		temp = arr[0];
		arr[0] = arr[1];
		arr[1] = temp;
		System.out.print("change方法修改后: "); // [99, 10]
		ParameterDemo.printArray(arr);
	}
	public static void main(String[] args) 
	{
		int[] arr = new int[]{10, 99};
		System.out.print("main方法修改后: "); // [10, 99]
		ParameterDemo.printArray(arr);
		ParameterDemo.swap(arr); 
		System.out.print("main方法修改后: "); // [99, 10]
		ParameterDemo.printArray(arr);
	}
}

对应的内存分配图如下:



8 多维数组

(1)二维数组概念

严格来说,在Java中不存在多维数组的概念,与C语言有别(C语言要求二维数组中每个元素(即一维数组)中的元素个数相等,但Java中不要求,见下例),一般称为数组中的数组;

一维数组 —— 数组中的每个元素都是一个值(基本类型或引用类型的值);(常见)

二维数组 —— 数组中的每个元素又是一个一维数组;(常见)

三维数组 —— 数组中的每个元素又是一个二维数组;(少见)


二维数组定义举例:

int[][] arr = {
	{1, 2, 3},
	{4, 5},
	{6}
};

对应内存分配图如下:



(2)二维数组的操作

a 初始化操作

静态初始化:

int[][] <二维数组名> = new int [][]{一维数组1, 一维数组2, 一维数组3, ...};
int[][] <二维数组名> = {一维数组1, 一维数组2, 一维数组3, ...};


动态初始化:

int[][] <二维数组名> = new int[<行数m>][<列数n>]; // 创建一个长度为m的二维数组,其中每个元素(一维数组)的长度是n


b 迭代操作

使用嵌套for循环进行遍历操作;

对于n维数组,遍历需要n个循环嵌套;

for(int i = 0; i < arr1.length; i ++){
	for(int j = 0; j < arr1[i].length; j++){
		// 相应操作
	}
}


9 Java5对数组的新语法支持

(1)增强for循环 —— foreach

有时在循环迭代数组时,不关心迭代变量(数组的索引);

只操作数组元素,不操作数组索引,其语法定义如下:

for(<数组元素的数据类型> <变量> : <数组名>){
	// 相应操作
}


如下,打印数组所有元素

int[] nums = {10, 20, 30, 40, 50};
for(int num : nums){
	System.out.println(num);
}


注:

foreach在底层依然使用for循环+索引操作数组,因此把增强for循环称为编译器的新特性——语法糖(让程序员写更少、更简单的代码完成相同的功能);

使用foreach循环时,对元素进行修改,不会改变数组中的元素,因为实际执行中先将元素的值赋值给一个变量,再对该变量进行修改,只影响变量的值,而非数组元素;

for循环比foreach循环功能更强大;

若迭代数组元素,不关心数组索引时,推荐使用foreach循环;


(2)方法的可变参数

这里可变指参数的个数可变

方法的可变参数也是编译器的新特性——语法糖,让程序员编写代码更简单,其底层就是一个数组类型;

定义的方法中,...位于参数类型和参数名之间;



注:

可变参数适用于参数个数不确定、数据类型确定的情况,Java把可变参数当作数组处理;

调用可变参数的方法时,编译器为该可变参数隐含创建一个数组在方法体中以数组形式访问可变参数,且可变参数不会出现空指针异常

可变参数必须作为方法的最后一个参数,避免参数的歧义;

方法的参数列表中最多只能有一个可变参数


10 数组算法

(1)实现int类型数组元素拷贝

从src数组中拷贝3,4,5,6元素到dest数组中的5,6,7,8索引位置上,代码实现如下:

class ParameterDemo 
{
	static void printArray(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		for(int i = 0; i < arr.length; i ++){
			System.out.print(arr[i]);
			if(i != arr.length - 1){
				System.out.print("\t");
			}
		}
		System.out.println();
	}
	static void copy(int[] src, int[] dest, int srcPos, int destPos, int length){
		for(int i = srcPos, j = destPos; i < srcPos + length; i ++, j ++){
			dest[j] = src[i];
		}
	}
	public static void main(String[] args) 
	{
		int[] src = new int[]{1,2,3,4,5,6,7,8,9,10};
		int[] dest = new int[10];
		ParameterDemo.printArray(dest);
		ParameterDemo.copy(src, dest, 2, 5, 4);
		ParameterDemo.printArray(dest);
	}
}

代码存在的问题 —— 只能拷贝int类型的数组,代码不够健壮(如srcPos<0等情况下的讨论);


(2)System类中的arraycopy方法

数组拷贝操作是经常使用的,SUN直接把数组的拷贝操作存放在JDK中的System类中;


public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

从指定源数组中复制一个数组,复制从指定位置开始,到目标数组的指定位置结束;

其中,Object是Java语言中的根类,可表示任意数据类型

该方法没有方法体,使用native修饰符(本地方法),底层使用了C/C++语言实现,Java直接调用其他语言编写好的功能;

查阅API文档(Java帮助文档),在什么类中有什么功能的方法 —— 文档在手,天下我有!


如,使用System.arraycopy()方法实现上例中的数组拷贝

class ArrayCopyDemo 
{
	static void printArray(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		for(int i = 0; i < arr.length; i ++){
			System.out.print(arr[i]);
			if(i != arr.length - 1){
				System.out.print("\t");
			}
		}
		System.out.println();
	}
	public static void main(String[] args) 
	{
		int[] src = {1,2,3,4,5,6,7,8,9,10};
		int[] dest = new int[10];
		ArrayCopyDemo.printArray(dest);
		System.arraycopy(src, 2, dest, 5, 4);
		ArrayCopyDemo.printArray(dest);
	}
}


(3)排序算法 —— 冒泡算法

a 概念

排序按照指定顺序(升序:从小到大/降序:从大到小)排列;


注:

在实际应用开发中,不需要自己编写排序算法代码,直接使用JDK自带的排序算法方法;只有在面试时才会被问到;


b 分类

选择排序 —— 直接选择排序、堆排序

交换排序 —— 冒泡排序、快速排序

插入排序 —— 直接插入排序、二分法插入排序、希尔排序

归并排序

这里只讲解冒泡和选择排序的升序算法;


冒泡排序

最简单的排序算法,基本思路是对未排序的各元素从头到尾依次比较相邻元素大小,若大于则交换顺序,经过第一轮比较排序后得到最大值,再使用同样方法把剩下的元素逐个比较即可;


如,有int类型数组待排序 int[] arr = {2, 9, 6, 7, 4, 1};

class BubbleSortDemo {
	static void printArray(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		for(int i = 0; i < arr.length; i ++){
			System.out.print(arr[i]);
			if(i != arr.length - 1){
				System.out.print("\t");
			}
		}
		System.out.println();
	}
	static void swap(int[] arr, int i, int j){
		int temp;
		temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	static void bubbleSort(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		int temp;
		for(int i = 0; i < arr.length - 1; i ++){
			for(int j = 1; j < arr.length - i; j ++){
				if(arr[j - 1] > arr[j]){
					BubbleSortDemo.swap(arr, j - 1, j);
				}
			}
		}
	}
	public static void main(String[] args) {
		int[] arr = {2, 9, 6, 7, 4, 1};
		BubbleSortDemo.printArray(arr);
		BubbleSortDemo.bubbleSort(arr);
		BubbleSortDemo.printArray(arr);
	}
}


选择排序

基本思路是选择某个索引位置的元素,然后和后面的元素依次比较,若大于则交换位置,经过第一轮比较排序后得出最小值,再使用同样的方法把剩下的元素依次比较即可;


如上例,对int类型数组使用选择排序,代码如下:

class SelectSortDemo 
{
	static void printArray(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		for(int i = 0; i < arr.length; i ++){
			System.out.print(arr[i]);
			if(i != arr.length - 1){
				System.out.print("\t");
			}
		}
		System.out.println();
	}
	static void swap(int[] arr, int i, int j){
		int temp;
		temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	static void selcetSort(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		for(int i = 0; i < arr.length - 1; i ++){
			for(int j = i + 1; j < arr.length; j ++){
				if(arr[i] > arr[j]){
					SelectSortDemo.swap(arr, i, j);
				}
			}
		}
	}
	public static void main(String[] args) 
	{
		int[] arr = {2, 9, 6, 7, 4, 1};
		SelectSortDemo.printArray(arr);
		SelectSortDemo.selcetSort(arr);
		SelectSortDemo.printArray(arr);
	}
}


(4)搜索算法

a 概念

从指定数组中搜索某一元素的索引;


b 分类

线性搜索

从头到尾/从末到头逐一搜索;

对于元素过多的数组,搜索性能极低;


二分搜索/二分查找/折半查找

前提是数组元素有序;


如,查找数组中等于4的索引值,代码如下:

class MidFindDemo 
{
	static void printArray(int[] arr){
		if(arr == null || arr.length == 0){
			return;
		}
		for(int i = 0; i < arr.length; i ++){
			System.out.print(arr[i]);
			if(i != arr.length - 1){
				System.out.print("\t");
			}
		}
		System.out.println();
	}
	static int midFind(int[] arr, int num){
		if(arr == null || arr.length == 0){
			return -1;
		}
		int low = 0;
		int high = arr.length - 1;
		int mid = (low + high) / 2;
		while(low <= high){
			if(arr[mid] < num){
				low = mid + 1;
			}else if(arr[mid] > num){
				high = mid - 1;
			}else{
				return mid;
			}
			mid =  (low + high) / 2;
		}
		return -1;
	}
	public static void main(String[] args) 
	{
		int[] arr = {1, 2, 4, 6, 7, 9};
		int num = 4;
		MidFindDemo.printArray(arr);
		System.out.println(num);
		System.out.println(MidFindDemo.midFind(arr, num));
	}
}


(5)自行封装数组操作的工具类ArrayUtil


(6)Java内置数组工具类Arrays

数组的算法操作频繁,SUN公司直接在JDK提供了一个数组的工具类java.util.Arrays

int binarySearch(type[] arr, type key) —— 使用二分法查找数组中某元素并返回其索引,若找不到则返回负数

void sort(type[] arr) —— 使用调优后的快速法对指定数组排序

String toString(type[] arr) —— 返回指定数组内容的字符串表示形式

public static type[] copyOf(type[] original, int newLength) —— 复制指定数组,截取或用0补充(如有必要),以使副本具有指定长度


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值