一、数组的定义、创建及Java内存分析
声明的时候数组并不存在,只有在创建的时候才会存在,因此定义数组的时候一定要初始数组大小(长度)。
public class Demo01 {
// 变量的类型 变量的名字 = 变量的值;
public static void main(String[] args) {
int[] nums; // 1.声明一个数组
int nums2[]; // 不建议这样书写
nums = new int[10]; // 2.创建一个数组
int[] num3 = new int[10]; // 必须初始数组大小。声明 + 创建
// 3.给数组元素赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
System.out.println(nums[9]); // 10
System.out.println(nums.length); //10
// 计算所有元素的和
int sum = 0;
for (int i = 0; i < nums.length; i ++){ // 获取数组长度:arrays.length
sum = sum + nums[i];
}
System.out.println("总和为:" + sum); // 55
}
}
二、数值的初始化
public class Demo02 {
public static void main(String[] args) {
// 静态初始化:创建 + 赋值
int[] a = {1,2,3,4,5}; // 定义后a的数组数量不可改变
System.out.println(a[0]); // 1
// 动态初始化:包含默认初始化。声明 + 初始化(创建空间)
int[] b = new int[10]; // 数组b中的10个元素的值当前为默认值int类型的0。
b[0] = 10;
b[1] = 10;
System.out.println(b[0]); // 10
System.out.println(b[1]);
System.out.println(b[2]); // 0
System.out.println(b[3]); // 0
}
}
数组常用的使用方法:
public class Demo03 {
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5};
// 打印全部数组元素
for (int i = 0; i < arrays.length; i++){
System.out.println(arrays[i]);
}
System.out.println("=====================");
// 计算所有元素的和
int sum = 0;
for (int i = 0; i < arrays.length; i ++){
sum += arrays[i];
}
System.out.println("sum=" + sum);
System.out.println("======================");
// 查找最大元素
int max = arrays[0];
for (int i = 1; i < arrays.length; i ++){
if (arrays[i] > max){
max = arrays[i];
}
}
System.out.println("max=" + max);
}
}
三、数组的使用
For - Each循环:
public class Demo04 {
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5};
// JDK1.5以上。
// 遍历数组中的每个元素。【没有下标】
for (int array : arrays) { // For- Each循环。遍历arrays数组中的每个元素
System.out.println(array);
}
}
}
数组作方法入参:
public class Demo004 {
public static void main(String[] args) {
int[] arrays = {1,2,3,4,5};
printArray(arrays);
}
// 打印数组元素
public static void printArray(int[] array){ // 数组作方法入参
for (int i = 0; i < array.length; i ++){
System.out.print(array[i] + " ");
}
}
}
数组作返回值:
public class Demo0004 {
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
int[] a = arrays;
【重点:】
Java中,数组是引用类型。将一个数组变量直接赋值给另一个数组变量时。实际上是复制引用,使两个变量都指向同一个数组。
数组的实际存储(数组元素)是在堆内存中,而数组的变量存储是在栈内存中,变量存储的是数组的引用(内存地址)。
因此两个数组间的直接赋值其实是在复制数组的引用。当指向的数组元素改变时,两个变量都会受影响。
而在使用方法返回新数组时,是将新数组的引用地址复制给了接收的数组。因此原数组的改变不会影响到接收的数组。
int[] reverse = reverse(arrays);
printArray(reverse);
}
// 反转数组
public static int[] reverse(int[] array){ // 数组作返回值
int[] result = new int[array.length];
// 反转操作:
for (int i = 0, j = result.length - 1; i < array.length; i++, j--) {
result[j] = array[i];
}
return result;
}
// 打印数组元素
public static void printArray(int[] array){
for (int i = 0; i < array.length; i ++){
System.out.print(array[i] + " ");
}
System.out.println();
}
}
四、多维数组
简单的说就相当于数组的嵌套。
public class Demo05 {
public static void main(String[] args) {
// [4][2],类似于一个4行2列的数组
/*
1,2 array[0]
2,3 array[1]
3,4 array[2]
4,5 array[3]
*/
int[][] array = { {1,2}, {2,3}, {3,4}, {4,5} };
System.out.println(array[0]); // 输出:[I@ed17bee。输出{1,2}这个对象的hashCode。
printArray(array[0]); // 传入{1,2}这个一维数组。会输出1和2,中间换行。
System.out.println(array[0][0]); // 1
System.out.println(array[0][1]); // 2
System.out.println(array.length); // 4
System.out.println(array[0].length); // 2
System.out.println("=====================");
// 打印二维数组中的每个具体元素
for (int i = 0; i < array.length; i++) { // 遍历二维数组中的一维数组对象
for (int j = 0; j < array[i].length; j++) { // 遍历二维数中一维数组对象的元素
System.out.println(array[i][j]);
}
}
// 如下:使用增强for循环遍历并打印二维数组中的元素
for (int[] object : array) { // 遍历二维数组的一维数组对象
for (int value : object) { // 遍历一维数组对象中的元素
System.out.print(value + "\t");
}
}
}
// 打印数组元素
public static void printArray(int[] array){
for (int i = 0; i < array.length; i ++){
System.out.println(array[i]);
}
}
}
下面是输出一个三维数组:
public class Demo005 {
public static void main(String[] args) {
int[][][] array = { { {1,2}, {1,2,3} } ,{ {1,2,3}, {2,3,4}} ,{ {1,2}, {3,1} ,{4,3} ,{3,4,5} }};
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
for (int k = 0; k < array[i][j].length; k++) {
System.out.print(array[i][j][k] + "\t"); // 输出每一个具体的一维数组元素,用TAB键(制表符)间隔。
}
}
System.out.println(); // 输出完一个二维数组换行。
}
}
}
五、数组的工具Arrays类
用Arrays类的,toString()方法打印数组元素:
import java.util.Arrays;
public class Demo06 {
public static void main(String[] args) {
int[] a = {1,2,3,4,9090,13131,534,45,23};
int[][] b = {{1,2,3},{2,3,4},{4,7}};
// 打印数组元素Arrays.toString(array)。
System.out.println(Arrays.toString(a)); // 输出:[1, 2, 3, 4, 9090, 13131, 534, 45, 23]
printArray(a);
System.out.println(Arrays.toString(b)); // 输出二维数组b中0、1、2的三个一维数组元素的对象。
System.out.println(Arrays.deepToString(b)); // 输出:[[1, 2, 3], [2, 3, 4], [4, 7]]。Arrays.deepToString(array)打印二维数组元素。
}
public static void printArray(int[] a){ // 按照Arrays.toString()方法格式打印一维数组元素。
for (int i = 0; i < a.length; i++) {
if (i == 0){
System.out.print("[");
}
if (i == (a.length - 1)){
System.out.println(a[i] + "]");
}else {
System.out.print(a[i] + " ,");
}
}
}
}
Arrays.sort(array):数组排序。
Arrays.fill(array,value):数组元素赋值。
import java.util.Arrays;
public class Demo006 {
public static void main(String[] args) {
int[] a = {1,2,3,4,9090,13131,534,45,23};
int[][] c = {{1,23,4},{1,3},{4}};
// 数组进行排序:升序
Arrays.sort(a); // 默认只适用于一维数组。
System.out.println(Arrays.toString(a)); // 输出:[1, 2, 3, 4, 23, 45, 534, 9090, 13131]
// fill,默认只适用于一维数组
Arrays.fill(a,val:0); // 数组填充。这里是将0的值赋值给数组a的所有元素。
System.out.println(Arrays.toString(a)); // 输出:[0, 0, 0, 0, 0, 0, 0, 0, 0]
Arrays.fill(a,fromlndex:2,tolndex:4,val:0);
// 数组填充。2为fromlndex(起始索引)、4为tolndex(结束索引)。将数值0赋值给下标2到下标4之前的元素(这里是下标2、下标3)。
System.out.println(Arrays.toString(a));
// 扩展:遍历二维数组c中的一维数组
for (int[] row:c){
Arrays.fill(row,0); // 将0赋值给二维数组c中一维数组的每个元素。Arrays.fill(int[] a, int val):将指定的int值val填充到整个int[]数组a的所有元素中。
}
System.out.println(Arrays.deepToString(c)); // 输出:[[0, 0, 0], [0, 0], [0]]
}
}
Arrays.equals(array1,array2):判断两个数组是否相等。返回布尔值。
Arrays.binarysearch(array,key):查找【已排序的】数组元素(key)。返回索引下标。
import java.util.Arrays;
public class Demo00006 {
public static void main(String[] args) {
int[] a = {1,2,3};
int[] b = {1,2,3};
int[] c = {2,1,3,3};
int[][] x = {{1,1},{2,3}};
int[][] y = {{1,1},{2,3}};
int[][] z = x;
// Arrays.equals()方法用于比较两个数组是否完全相同(值、顺序、数量、类型均相同)。返回值为布尔类型。
boolean d = Arrays.equals(a ,b);
System.out.println(d); // true
boolean e = Arrays.equals(a ,c);
System.out.println(e); // false
boolean f = Arrays.equals(x,y); // Arrays.equals()方法对于多维数组比较的是每个子数组的引用(内存地址)。
System.out.println(f); // false。因为数组x和y的子数组内存地址不同。
boolean g = Arrays.equals(x,z);
System.out.println(g); // true
【注意点:】
Arrays.binarySearch(array,key)是Java提供的二分查找方法,用于在【已升序排序的数组】中查找指定元素key。
如果找到,返回该元素的索引(下标)(从0开始)。如果没找到,返回一个 -(插入点 + 1)。插入点就是按照数组排序,应该插入的位置下标。
如果在一个升序排序的数组里面,有多个相同元素,binarySearch无法保证返回第一个或最后一个,只保证返回其中一个的索引。
数组必须是升序排列的,否则结果不可靠。
// Arrays.binarySearch(a,key:3):查找数组a里面键为3的索引(下标)。默认只支持一维数组的查找。
int result = Arrays.binarySearch(a,3);
System.out.println(result); // 2
int result1 = Arrays.binarySearch(a,23);
System.out.println(result1); // -4。-4 = -(插入点:3 + 1)。插入点就是按照数组排序,应该插入的位置下标。
int result2 = Arrays.binarySearch(c,2);
System.out.println(result2); // -3。因为数组c不是升序列表,所以结果不可靠。
int result3 = Arrays.binarySearch(c,3);
System.out.println(result3); // 2
Arrays.sort(c);
int result4 = Arrays.binarySearch(c,3);
System.out.println(result4); // 2
}
}
以下是查看Arrays类的任意方法的源码小技巧:
六、冒泡排序
将变量1的值先赋予给中间变量,然后将变量2的值赋值给变量1(此时为变量2的值)。最后将中间变量的值(原先变量1的值)赋予变量2(此时为原先变量1的值),这样就完成了两个变量值的交换。
下面是一个比较标准的冒泡排序:
import java.util.Arrays;
public class Demo07 {
public static void main(String[] args) {
int[] a = {1,7,5,6,72,2,2,2,25,6,7};
int[] sort = sort(a);
System.out.println(Arrays.toString(sort));
}
/*
冒泡排序:
1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置。
2.每一次比较,都会产生出一个最大,或者最小的数。
3.下一轮则,少一次排序。
4.依次循环,直到结束。
*/
public static int[] sort(int[] array){
int temp = 0; // 临时变量
// 外层循环,判断要走多少次循环
for (int i = 0; i < array.length - 1; i++) {
boolean flag = false; // 通过flag标识位减少没有意义的比较
// 内层循环,比较两个数,如果第一个数,比第二个数大,则交换位置
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j+1]){ // 改变判断符号为: <。将改变排序顺序为降序
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true; // 当某一次内层循环的比较都不成立时,代表已经排序完毕。因此标识位flag不会被赋予true值。就会运行到下面的判断,然后结束整个循环。
}
【运行详细分析:】
先是将array[0]和array[1]比较,决定是否交换位置。
然后将array[1]和array[2]比较,决定是否交换位置。
依次遍历,到array[j]和array[j+1]比较时,产生的结果将是数组中当前遍历的最大的数,并交换到最右边。
理论逻辑:如果array[0]就是数组中当前最大的数,那么第一次比较,这个最大的数会被交换到array[1]的位置,
第二次比较则是对array[1]和array[2]的比较,那么又会被交换到array[2]的位置。如果array[0]是最大的数,那么每一次遍历比较的时候都会有它,因为每一次比较它都会被往右移动。
直到array[0]被移动到最右边,这样就完成了一次完整的内循环。
因此完成一次内循环,产生的结果是,当前遍历中最大的数会被移动到最右边。
【核心逻辑:内层循环完成一次完整的遍历时,就会将当前遍历中最大的数移动到最右边,然后外层循环i+1,因此内层循环减少一次遍历(就是减少了对最右边排好了的一次遍历)】
}
if (flag == false){
break; // break语句只会跳出当前所在的循环(即直接包含它的那一层循环),并不会跳出外层循环。
}
}
return array;
}
}
以下是冒泡排序的精简核心代码,和关于循环的判断条件的核心解释:
import java.util.Arrays;
public class Demo007 {
public static void main(String[] args) {
int[] array = {234,15,7686,32,9,3,1,970,3,7};
sort(array); // 方法的返回值可以不被接收,编译器不会报错。Java允许忽略返回值。
System.out.println(Arrays.toString(array)); // Arrays.toString()方法只返回一个字符串,如返回"[1,2,3]"。它本身并不会打印输出,所以要配合输出语句输出。
String a = Arrays.toString(array);
System.out.println(a); // 输出:[1, 3, 3, 7, 9, 15, 32, 234, 970, 7686]
}
public static int[] sort(int[] arrays){ // 数组是引用类型,方法参数传递的是数组的引用地址。因此在方法内部改变数组,实参也会受影响。
for (int i = 0; i < arrays.length -1; i++) { // 数组元素的下标是从[0]到[length-1]。需要比较的下标j是从[0]到[length-2],即[0]到[length-2]就是外循环的次数。
for (int j = 0; j < arrays.length -1 -i; j++) { // 因为判断是arrays[j]和arrays[j+1]的比较,因此在[j=length-2]时就是在和[length-1]比较。
if (arrays[j] > arrays[j+1]){ // i就是最外层的循环数,也是最右边已排序的元素个数(即内层完整的循环数)。因此减i就是避免遍历已经排序了的元素,并且不断缩小遍历范围,以此来达到循环的结束。
int temp = arrays[j];
arrays[j] = arrays[j+1];
arrays[j+1] = temp;
}
}
}
return arrays;
}
}
七、稀疏数组
代码如下:使用稀疏数组记录五子棋棋盘
public class Demo08 {
public static void main(String[] args) {
// 1.创建一个二维数组 11 * 11 0:表示没有棋子。 1:表示黑棋。 2:表示白棋。
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
// 输出原始的数组
System.out.println("输出原始的数组");
for (int[] object : array1) { // 遍历array1中的一维数组
for (int value : object) { // 遍历array1中一维数组的每个元素。二维数组中每个一维数组相当于是一行。
System.out.print(value + "\t"); // 制表符隔开每个一维数组中的元素
}
System.out.println(); // 输出完一行(一维数组),换行。
}
// 2.转换为稀疏数组保存
// 2.1 获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) { // 行和列的下标:从0到10
for (int j = 0; j < 11; j++) {
if (array1[i][j] != 0){
sum++ ;
}
}
}
System.out.println("有效值的个数:" + sum);
// 2.2创建一个稀疏数组
int[][] array2 = new int[sum+1][3]; // 行:为有效值+1,因为第1行记录着原数组的总行、总列、有效值总个数。列为固定3,3列分别记录着:行、列、值
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
// 遍历二维数组,将非零的值,存放到稀疏数组中
int count = 0; // 计数
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0){
count ++;
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];
}
}
}
// 输出稀疏数组
System.out.println("稀疏数组");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0] + "\t"
+ array2[i][1] + "\t"
+ array2[i][2] + "\t");
}
// 3.还原
// 3.1.读取稀疏数组
int[][] array3 = new int[array2[0][0]][array2[0][1]];
// 3.2.给其中的元素还原它的值
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]] = array2[i][2];
}
// 3.3.打印
System.out.println("还原的数组");
for (int[] object : array3) {
for (int value : object) {
System.out.print(value + "\t");
}
System.out.println();
}
}
}
以下是稀疏数组的精简核心代码,和核心解释:
public class Demo008 {
public static void main(String[] args) {
// 1.创建原始数组
int[][] array1 = new int[11][11]; // 11行11列。代表着一共11个一维数组对象,每个一维数组对象有11个元素。
array1[1][2] = 1;
array1[2][3] = 2;
// 2.创建稀疏数组
/* 稀疏数组示例:
行\列 [0] [1] [2]
------------------------
[0] 总行 总列 总个数
[1] 行1 列1 值1 【稀疏数组中除第1行外,每一行记录着一个特殊元素的行、列、值。】
[2] 行2 列2 值2 【稀疏数组的第0列记录着行,第1列记录着列,第2列记录着值】
*/
int sum = 0;
for (int[] object : array1) { // 遍历二维数组中的一维数组对象
for (int value : object) { // 遍历一维数组对象中的具体元素
if (value != 0) {
sum++; // 获取特殊元素个数
}
}
}
int[][] array2 = new int[sum + 1][3]; // 稀疏数组的行为特殊元数个数+1。因为第1行记录着原数组的总行、总列、特殊值总个数。
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
int row = 1; // row从1开始,作为稀疏数组的行。
for (int i = 0; i < array1.length; i++) { // 遍历二维数组的行(一维数组对象)
for (int j = 0; j < array1[i].length; j++) { // 遍历二维数组的列(一维数组对象中的元素)
if (array1[i][j] != 0) {
array2[row][0] = i; // i为特殊值的行的下标
array2[row][1] = j; // j为特殊值的列的下标
array2[row][2] = array1[i][j];
row++;
}
}
}
System.out.println("稀疏数组:");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0] + "\t"
+ array2[i][1] + "\t"
+ array2[i][2] + "\t");
}
System.out.println("=============================================");
// 3.通过稀疏数组还原原数组
int[][] array3 = new int[array2[0][0]][array2[0][1]];
for (int i = 1; i < array2.length; i++) { // 从稀疏数组的第1行开始遍历,将所记录的特殊元素的信息进行还原。
array3[array2[i][0]][array2[i][1]] = array2[i][2]; // 根据记录的行、列、值精准赋值。array[记录的行][记录的列]=记录的值。
}
for (int[] object : array3) {
for (int value : object) {
System.out.print(value + "\t");
}
System.out.println();
}
}
}