一、数组的定义
1.数组的概念
数组是同一种类型数据的集合,是一个容器,用于存储多个数据。
2.数组的优点
数组中的每个元素都有下表值,下标从0开始。下标值方便程序员对数组中的数据进行操作。
3.数组定义的格式
1)元素类型[]数组名=new 元素类型[元素个数或数组长度]; 这种定义方法称之为动态定义法。
示例:int[] arr = new int[5];
2) 元素类型[]数组名= new 元素类型[]{元素,元素,……}; 这种定义方法称之为静态定义法。
示例:int[] arr = new int[] {3,5,1,7};
int[] arr = {3,5,1,7};
二、引用数据类型内存结构
1.Java内存结构
由于Java对不同类型的数据有不同的处理方式,所以Java对内存进行了不同的区域划分。这提高了运算效率,优化了内存管理方式。
1)栈内存
栈内存用于储存局部变量。当数据使用完毕之后,其所占空间会自动释放。
2)堆内存
a. 堆内存用于储存对象和数组,所有通过new
建立的实例都在堆内存中存放;
b. 每一个对象实例都有一个内存地址;
c. 实例中的变量都有默认的初始化值;
d. 实例不再被使用时(没有任何引用指向该实例),该实例会在不确定的时间被垃圾回收器回收,释放内存
2.数组的内存结构示意图
数组属于引用数据类型,其内存结构示意图如下:
当执行int[] x = new int[3]
时,
1)在栈内存开辟一个内存空间,用于存储变量x
;
2)在堆内存开辟内存空间,用于存储new
出来的数组对象实例;
3)将数组对象的内存首地址(0x0079)
赋给x
;
4)至此,x
就是数组对象的引用,指向数组对象
3.两个引用指向同一数组对象实例
int[] x = new int[3];
int[] y = x;
y[1] = 89;
x = null;
该代码段中,将数组的内存地址引用赋给了x
,也赋给了y
。那么x
,y
就指向了同一数组对象。x
,y
的内存情况如下图:
因此,当x = null
执行之后,内存中并没有产生垃圾,因为变量y
也指向该数组,该数组仍处于被引用状态。同时,y[1] = 89
执行之后,x[1]
的值也将变为89
,而不是0
。
4.基本数据类型内存结构对比
引用数据类型中,两个变量指向同一对象实例,其中一个变量对对象的值进行修改,会影响另一个变量,这是引用数据类型的规律。下面对比一下基本数据类型做同样的赋值操作之后的内存示意图。
int a = 5;
int b = a;
b = 8;
运行结束后,a
的值仍旧为5
。内存图如下:
三、数组操作过程中常见的异常
1.ArrayIndexOutOfBoundsException数组下标越界异常
int[] arr = new int[3];
System.out.println(arr[3]);
arr[3]
超出下标范围,出现异常。
2.NullPointerException空指针异常
int[] arr = new int[3];
arr = null;
System.out.println(arr[1]);
arr
被赋值为空,已经无法指向数组对象,再使用arr
操作数组,出现空指针异常。
四、排序算法
1.选择排序
1)从下标为0
的数开始,逐一和后面的数进行比较,遇到小的数,则交换位置
示例代码:
package test;
/**
* 选择排序
* @author jeremy
* 从下标0开始,将每一个数与后一个数进行比较,如果后一个数大于前一个数,则交换位置
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[]{3, 11, 7, 5, 18, 34, -6};
//排序前
printArray(arr);
//排序
selectSort(arr);
//排序后
printArray(arr);
}
public static void selectSort(int[] arr) {
for(int x = 0; x < arr.length - 1; x ++ ) {
for(int y = x + 1; y < arr.length; y ++) {
if(arr[x] > arr[y]) {
int tmp = arr[x];
arr[x] = arr[y];
arr[y] = tmp;
}
}
}
}
public static void printArray(int[] arr) {
System.out.print("[");
for(int x = 0; x < arr.length; x ++) {
//判断如果是不是最后一个元素,在元素后面加逗号,如果是,直接打印并换行
if(x != arr.length - 1) {
System.out.print(arr[x] + ", ");
} else {
System.out.println(arr[x] + "]");
}
}
}
}
2)从下标为0的数开始,逐一和后面的数进行比较,如果遇到比自身小的数,让小的数继续和后面的数进行比较,直至发现最小的数,再和比较开始时的第一个数交换位置。效率稍高于第一种选择排序
示例代码:
package test;
/**
* 从下标为0的数开始,逐一和后面的数进行比较,如果遇到比自身小的数,让小的数继续和后面的数进行比较
* 直至发现最小的数,再和比较开始时的第一个数交换位置。
* @author jeremy
*
*/
public class SelectSort2 {
public static void main(String[] args) {
int[] arr = new int[]{3, 11, 7, 5, 18, 34, -6};
//排序前
printArray(arr);
//排序
selectSort(arr);
//排序后
printArray(arr);
}
public static void selectSort(int[] arr) {
//声明最小值的下标,和第三方变量
int minPos, tmp;
for(int x = 0; x < arr.length - 1; x ++ ) {
minPos = x; //假设最小值下标等于第一个数的下标,即为0
for(int y = x + 1; y < arr.length; y ++) {
if(arr[y] < arr[minPos]) {
minPos = y; //如果后一个数小于minPos下标所在的那个数,则交换下标值,即保证minPos为最小值
}
}
//如果minPos不是最初的那个x的值,即minPos所在位置的数字已经发生变化,则将minPos下标所在位置的值和初始值做交换,得到数组中最小值
if(minPos != x) {
tmp = arr[minPos];
arr[minPos] = arr[x];
arr[x] = tmp;
}
}
}
public static void printArray(int[] arr) {
System.out.print("[");
for(int x = 0; x < arr.length; x ++) {
//判断如果是不是最后一个元素,在元素后面加逗号,如果是,直接打印并换行
if(x != arr.length - 1) {
System.out.print(arr[x] + ", ");
} else {
System.out.println(arr[x] + "]");
}
}
}
}
2.冒泡排序
每一次循环,确定一个最大的数,并让最大的数下沉到最后,让较小的数逐次上升。
示例代码:
package test;
/**
* 冒泡排序:相邻两个数进行比较,遇到较小的数则交换位置。
* 每一次比较完成,则确定一个最大的数。
* @author jeremy
*
*/
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[]{3, 11, 7, 5, 18, 34, -6};
printArray(arr);
bubbleSort(arr);
printArray(arr);
}
public static void bubbleSort(int[] arr) {
int tmp;
for(int x = 0; x < arr.length; x ++) {
//arr.length - x保证每次比较之后,最后一个元素不参与比较
//arr.length - x -1保证下标y + 1不会越界
for(int y = 0; y < arr.length - x - 1; y ++) {
if(arr[y] > arr[y + 1]) {
tmp = arr[y];
arr[y] = arr[y + 1];
arr[y + 1] = tmp;
}
}
}
}
public static void printArray(int[] arr) {
System.out.print("[");
for(int x = 0; x < arr.length; x ++) {
//判断如果是不是最后一个元素,在元素后面加逗号,如果是,直接打印并换行
if(x != arr.length - 1) {
System.out.print(arr[x] + ", ");
} else {
System.out.println(arr[x] + "]");
}
}
}
}
3.真实开发,请使用Arrays.sort()方法…
五、数组查找
1.折半查找
示例一:
package test;
/**
* 值班查找的前提是数组是有序数组
* 折半查找1:当中间值等于目标值的时候,结束查找,并返回查找结果
* @author jeremy
*
*/
public class HalfSearch1 {
public static void main(String[] args) {
int[] arr = new int[]{-6, 2, 5, 8, 19, 21, 33, 66, 120, 211};
System.out.println("index = " + halfSearch(arr, 2));
}
public static int halfSearch(int[] arr, int key) {
int min = 0;
int max = arr.length - 1;
int mid = (arr.length - 1) / 2;
while(arr[mid] != key) {
if(arr[mid] < key) {
min = mid + 1; //目标数大于中间数,较小值的下标等于中间数下标+1
} else {
max = mid - 1; //目标数小于中间数,较大值的下标等于中间数下标-1
}
//如果较小值的下标大于了较大值的下标,说明目标数不存在,则返回-1
if(min > max) {
return -1;
}
mid = (min + max) / 2;
}
return mid;
}
}
示例二:
package test;
/**
* 折半查找2:当较小值下标小于等于较大值下标时,继续查找,直到找到目标数;如果不存在,返回-1
* @author jeremy
*
*/
public class HalfSearch2 {
public static void main(String[] args) {
int[] arr = new int[]{-6, 2, 5, 8, 19, 21, 33, 66, 120, 211};
System.out.println("index = " + halfSearch(arr, -6));
}
public static int halfSearch(int[] arr, int key) {
int min = 0;
int max = arr.length - 1;
int mid;
while(min <= max) {
mid = (min + max) >> 1;
if(arr[mid] < key) {
min = mid + 1;
//这里一定要写上else if,因为while条件中没有用数组的具体值做比较
//因此如果找到了相等的值,而没有判断条件来做出返回动作,程序会出错,即查找第二个数字的时候
//返回的还是下标0
} else if(arr[mid] > key) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}
}
2.折半查找拓展
在有序数组中插入一个数,使数组仍保持有序。
示例代码:
package test;
/**
* 在一个有序数组中插入一个数字,使得插入数字后的数组仍然保持有序
* @author jeremy
*
*/
public class InsertNumber {
public static void main(String[] args) {
int[] arr = new int[]{-6, 2, 5, 8, 19, 21, 33, 66, 120, 211};
System.out.println("Insert Position is: " + insertPos(arr, 34));
}
public static int insertPos(int[] arr, int key) {
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;
while(min <= max) {
if(arr[mid] < key) {
min = mid + 1;
} else if(arr[mid] > key) {
max = mid - 1;
} else {
return mid;
}
mid = (max + min) / 2;
}
return min; //如果找不到目标数字,直接返回目标数组前一个数的下标,就是目标数字的插入位置
}
public static void printArray(int[] arr) {
System.out.print("[");
for(int x = 0; x < arr.length; x ++) {
//判断如果是不是最后一个元素,在元素后面加逗号,如果是,直接打印并换行
if(x != arr.length - 1) {
System.out.print(arr[x] + ", ");
} else {
System.out.println(arr[x] + "]");
}
}
}
}
六、进制转换
1.十进制转二进制
示例代码:
package test;
/**
* 十进制转换为二进制数
* @author jeremy
*
*/
public class ToBinary {
public static void main(String[] args) {
toBin(6);
}
public static void toBin(int num) {
StringBuffer sb = new StringBuffer();
while(num > 0) {
sb.append(num % 2); //模2,将余数存储到StringBuffer当中
num /= 2; //原数字除以2,准备继续做模运算
}
System.out.println(sb.reverse()); //用sb对象的reverse()方法,反向打印出结果
}
}
2.十进制转十六进制
示例代码:
package test;
/**
* 十进制转十六进制
* @author jeremy
*
*/
public class ToHex {
public static void main(String[] args) {
toHex(60);
}
public static void toHex(int num) {
int tmp;
StringBuffer sb = new StringBuffer();
for(int x = 0; x < 8; x ++) {
tmp = num & 15;
if(tmp > 9) {
sb.append((char)(tmp - 10 + 'A')); //如果&15之后的数大于9,则转换成字母
} else {
sb.append(tmp);
}
num = num >>> 4; // & 操作结束,原数字右移4为,准备下一次 & 操作
}
System.out.println(sb.reverse()); //反向输出结果
}
}
3.查表法十进制转十六进制
示例代码:
package test;
/**
* 查表法十进制转十六进制
* @author jeremy
*
*/
public class ToHexTable {
//声明十六进制表
private static char[] ch = new char[]{'0', '1', '2', '3'
, '4', '5', '6', '7'
, '8', '9', 'A', 'B'
, 'C', 'D', 'E', 'F'};
private static char[] result = new char[8];
public static void main(String[] args) {
toHex(60);
}
public static void toHex(int num) {
int tmp;
int pos = result.length; //声明一个指针,用于字符数组从最后一个开始储存十六进制数
while(num != 0) { //判断num是否大于0,如果等于0,则不需要再进行右移操作,有效位已经全部转换为十六进制
tmp = num & 15; //& 15 取出有效位的十六进制数
result[--pos] = ch[tmp]; //从数组的最后以为开始储存查表得出的十六进制数
num = num >>> 4; //右移4位
}
//从有效位开始打印
for(int x = pos; x < result.length; x ++) {
System.out.print(result[x]);
}
}
}
4.抽取进制转换共性代码
示例代码:
package test;
/**
* 将查表法中的代码抽取,提供方法计算十进制转其他进制
* @author jeremy
*
*/
public class ToAllNumberFormat {
public static void main(String[] args) {
toBin(6);
toBin(-6);
toOct(60);
toOct(-60);
toHex(60);
toHex(-60);
toBin(0);
}
//十进制转二进制
public static void toBin(int num) {
formatTrans(num, 1, 1);
}
//十进制转八进制
public static void toOct(int num) {
formatTrans(num, 7, 3);
}
//十进制转十六进制
public static void toHex(int num ) {
formatTrans(num, 15, 4);
}
public static void formatTrans(int num, int base, int offset) {
//判断如果要转换的十进制数为0,直接输出0,并结束方法
if(num == 0) {
System.out.println(0);
return;
}
//声明十六进制表,该表包含八进制表和二进制表
char[] ch = new char[]{'0', '1', '2', '3'
, '4', '5', '6', '7'
, '8', '9', 'A', 'B'
, 'C', 'D', 'E', 'F'};
//声明32位数组,该数组包含能储存八进制和十六进制数
char[] result = new char[32];
//声明指针,用于存储数据
int pos = result.length;
while(num != 0) {
result[--pos] = ch[num & base];
num = num >>> offset;
}
for(int x = pos; x < result.length; x ++) {
System.out.print(result[x]);
}
System.out.println();
}
}
七、二维数组
1.二维数组的定义
1)int[][]arr=newint[2][4];
1> 定义了名称为arr
的二维数组;
2> 二维数组中有2
个一维数组;
3> 每一个一维数组中有4
个元素;
4> 一维数组的名称分别为arr[0]
,arr[1]
;
5> 给第一个一维数组1下标位赋值为78
写法是:arr[0][1]=78
;
2)int[][]arr=newint[3][];
1> 二维数组中有3个一维数组;
2> 每个一维数组被默认初始化为空值null
;
3> 可以对这个三个一维数组分别进行初始化:
arr[0] = new int[3];
arr[1] = new int[1];
arr[2] = new int[2];
3)int[][]arr={{3,5,2},{2,6,7},{9,2,1,6}};
1> 定义一个名称为arr
的二维数组;
2> 二维数组中的有三个一维数组;
3> 每一个一维数组中具体元素也都已初始化;
4> 第一个一维数组arr[0]={3,5,2};
5> 第二个一维数组arr[1]={2,6,7};
6> 第三个一维数组arr[2]={9,2,1,6};
7> 第三个一维数组的长度表示方式:arr[2].length;
2.二维数组声明和求和
package test;
/**
* 声明一个二维数组,遍历求和
* @author jeremy
*
*/
public class TwoDArray {
public static void main(String[] args) {
int[][] arr = new int[][]{{2, 3, 5, 1}, {5, 77, 6, 10}, {4, 13, 17, 7}, {8, 9, 0, 11}};
int sum = 0;
//遍历二维数组,并求和
for(int x = 0; x < arr.length; x ++) {
for(int y = 0; y < arr[x].length; y ++) {
sum += arr[x][y];
}
}
System.out.println("sum = " + sum);
}
}
3.小细节int[] x, y[];
这样的声明方式,x是一维数组,y是二维数组。
八、知识拓展
数组的复制。使用java.lang.System
类的静态方法:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
可以用于数组src
从第srcPos
项元素开始的length
个元素拷贝到目标数组dest
从destPos
项开始的length
个位置。如果源数据数目超过目标数组边界将抛出IndexOutOfBoundsException
异常。
示例代码:
package test;
/**
* 测试System类的arraycopy方法
* @author jeremy
*
*/
class TestArrayCopy {
public static void main(String[] args) {
String[] s = {"Microsoft", "IBM", "SUN", "Oracle", "Apple"};
String sBak[] = new String[6];
//public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
//src:原数组;srcPos:从原数组第srcPos个元素开始拷贝;dest:目标数组;destPos:从目标数组中的destPos位开始拷贝;
//length;拷贝多少个原数组中的元素
System.arraycopy(s, 0, sBak, 0, s.length);
for(int i = 0; i < sBak.length; i ++) {
System.out.print(sBak[i] + " ");
}
System.out.println();
int[][] array = {{1, 2}, {1, 2, 3}, {3, 4}};
int[][] arrayBak = new int[3][];
System.arraycopy(array, 0, arrayBak, 0, array.length);
arrayBak[2][1] = 100;
for(int i = 0; i < array.length; i ++) {
for(int j = 0; j < array[i].length; j ++) {
System.out.print(array[i][j] + " ");
}
System.out.println();
}
}
}