Java语言基础组成:关键字、标识符、注释、常量和变量、运算符、语句、函数、数组
同一种类型数据的集合,其实数组就是一个容器。
运算时很多数据进行运算,先想到的不是运算,而是要把这些数据临时存储起来,以便于后期的运算。数组就是存储数据的一种方式。
【数组的好处】:数组能自动给存入元素进行编号,编号从0开始,方便操作这些元素。
【数组的格式 1】: | 元素类型[ ] 数组名 = new 元素类型[元素个数或数组长度];
|
【数组的格式 2】: (静态存储方式) |
元素类型[ ] 数组名 = new 元素类型[ ]{ 元素1, 元素2, … };
|
任何一个应用程序在运行时,都要在内存中开辟空间,cpu处理完A程序才会再处理B程序。
Java程序在运行时,需要在内存中分配5个空间:栈内存、堆内存、方法区、本地方法区、寄存器。
【分析】:
【栈的特点】:数据使用完毕,会自动释放,变量会自动释放。
【内存为什么划分出这么多空间,划分这么细?】:
因为每一片内存空间中的数据处理方式不一样,对于栈而言,数据使用完会自动释放。
【实例 2】 分析在内存中的分配情况。
int[] x = new int[3];
x[0] = 59;
【分析】:
在栈内存中定义了x,凡是局部变量都在栈内存中开辟空间。
局部变量指 :
(1)定义在方法中的变量
(2)定义在方法参数上的变量。
(3)定义在for循环里的变量等。
new 出来的实体都在堆里边,堆里边存放的就是实体。实体为:数组和对象。
【赋值】:将地址值赋值给变量x,赋值的不是数组本身,而是数组在内存中的地址。
数组并没有真正地存放在x 中去,只存放了地址,它在引用数组而已。
【指向】:变量x 有值了,称为x 指向了这个数组,或x 引用 了这个数组。
【实例 3】 分析在内存中的分配情况。
int[] x = new int[3];
x = null;
【分析】:
【堆的特点】:
堆内存中的每个实体,都有一个存放位置(内存地址值),内存里都是二进制的地址值,用地址来标识数据存放的位置,那么这个数组在内存中存放的时候,总有一个起始位置,即所谓的数组指向。
数组一旦被定义,里边的元素都有值,堆内存中的实体用了封装数据的,都有默认初始化值。
int型数组值:0
double型数组值:0.0
float型数组值:0.0f
boolean型数组值:false
String型数组值:null
char型数组值:’\u0000’ (相当于空格)
【垃圾回收机制】:数组在堆内存中没人使用了,当一个实体在堆内存中没有任何引用所使用它的话,就视它为垃圾,或Java训圾。这个垃圾不会立刻被内存清除掉,而是在不定时的时间内,启动一个叫垃圾回收机制,将数组实体在堆内存中清除。
C++语言:由程序员手动地调用一个功能将内存中的数据清除。
Java:程序员不用手动清除,只要这个对象或实体,在堆内存中已经变为垃圾,JVM会自动启动垃圾回收机制,将堆内存中不再使用的实体清除。
【实例 4】分析在内存中的分配情况。
int[] x = new int[3];
int[] y = x;
y[1] = 89;
x = null;
/*【堆内存中有无垃圾?】
=> 没有垃圾,x,y引用指向了同一个数组对象,当x的值为空,不再指向数组时,但是y仍指向数组。
*/
【实例 5】分析在内存中的分配情况。
int[] x = new int[3];
int[] y = new int[3];
y[1] = 89;
x = null;
/**【堆内存中有无垃圾?】
=> 有垃圾。
只要是new的,内存中就会开辟新的空间,y指向的是新的数组。
当x = null时,x之前指向的数组,就无人指向,因而变为了垃圾。
*/
【实例 1】:数组角标越界异常(ArrayIndexOutOfBoundsException )
public static void main(String[] args) {
int[] arr = {3, 1, 6};
System.out.println(arr[2]); //结果为:6
System.out.println(arr[3]); //[错误]
}
【分析】:
【实例 2】:空指针异常(ArrayIndexOutOfBoundsException )
public static void main(String[] args) {
int[] arr = {3, 1, 6};
arr = null;
System.out.println(arr[1]); //[错误]
}
【分析】:
最为常见的获取数组中的元素,通常用遍历,用for语句循环。【注】:角标最大值 = 数组长度 - 1
数组中有一个属性可以直接获取到数组元素个数:数组名称.length
【注】:System.out.println(arr) 代表:把一个数组实体的引用给直接打印了。
输出结果为[I@62bc184
【实例 1】:求数组和(【累加思想】:变量 + 循环)
public static void main(String[] args) {
int[] arr = {3, 1, 6};
System.out.println("sum = " + getArraySum(arr));
}
/**
* 功能:求出数组的元素和
* @param arr 数组
* @return 数组元素和
*/
public static int getArraySum(int[] arr)
{
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
【实例 2】:定义一个功能,用于打印数组中的元素,元素间用逗号隔开。
/**
* 功能:用于打印数组中的元素,元素间用逗号隔开
* @param arr 数组
*/
public static void printArray(int[] arr)
{
for (int i = 0; i < arr.length; i++)
{
if (i != arr.length - 1) //元素间逗号隔开
{
System.out.print(arr[i] + ",");
}
else
{
System.out.println(arr[i]);
}
}
}
【实例 3】:给定一个数组{5,1,6,4,2,8,9} 获取数组中的最大值,以及最小值。(只适用于数值型数组)
/**
* 方法一:求max
* 1. 定义变量max,初始化为数组中仁义一个元素值即可,max = arr[0]
* 2. 通过循环语句,对数组进行遍历
* 3. 在变量中定义判断条件,如果遍历到的元素比变量中的元素大,就赋值给变量。
* @param arr 数组
* @return 最大值
*/
public static int getMax(int[] arr)
{
int max = arr[0];
for (int i=1; iif(max < arr[i])
max = arr[i];
}
return max;
}
/**
* 方法二:求max
* 1. 定义变量max,初始化为数组中的任意一个角标,max = 0
* 2. 通过循环语句,对数组进行遍历
* 3. 在变量中定义判断条件,如果遍历到的元素比arr[max]中的元素大,就将元素角标赋值给变量。
* @param arr 数组
* @return 最大值
*/
/*public static int getMax(int[] arr)
{
int max = 0;
for (int i = 1; i < arr.length; i++)
{
if(arr[max] < arr[i])
max = i;
}
return arr[max];
}*/
//获取double类型数组的最大值,因为功能一致,所以定义相同函数名称,以重载方式存在。
public static double getMax(double[] arr)
{
}
/**
* 求min
* @param arr 数组
* @return 最小值
*/
public static int getMin(int[] arr)
{
int min = arr[0];
for (int i=1; iif(min > arr[i])
min = arr[i];
}
return min;
}
【排序的方法】:
冒泡排序 :相邻两个元素进行排序,如果符合条件则换位。
特点:内循环结束一次,最值出现在最后位。
选择排序:选择固定位置的值,不断地和其他元素进行比较,符合条件则换位,接着拿这个位置上的值进行比较。
特点:内循环结束一次,最值出现在头角标位置上。
插入排序:需要排序的数组越有序,插入排序的效率越高。
希尔排序(是最快的排序,三层循环加上位运算):
将需要排序的数组分割成小的子数组,对这个子数组进行插入排序,排序后越来越接近有序的数组。
快速排序
归并排序
工具类Arrays:
在Java中, 已经定义好了一种排序方式,是使用到了专门操作数组的工具类Arrays。
Arrays有一个静态方法sort可以对数组进行排序。例如:Arrays.sort(arr);
【实例】 对给定数组进行排序。{5,1,6,4,2,8,9}
【选择排序】:选择固定位置的值不断和别人进行比较,交换位置后,依旧拿这个位子的值与别人比较。
【选择排序的特点】:内循环结束一次,最值出现在头角标位置上,前面逐级进行排序
【实例】:代码体现
public static void main(String[] args) {
int[] arr = {5,1,6,4,2,8,9};
System.out.println("-------------排序前-------------");
printArray(arr);
selectSort(arr); //选择排序
System.out.println("-------------排序后-------------");
printArray(arr);
}
/**
* 选择排序
* @param arr 数组
*/
public static void selectSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++) { //不需要遍历最后一个角标,所以arr.length - 1
for(int j = i + 1; j < arr.length; j++) //每个元素与后面进行比较,所以i + 1
{
if(arr[i] > arr[j])
{
swap(arr, i, j); //两数交换
}
}
}
}
/**
* 交换数组中的两个元素
* @param arr 数组
* @param i 数组角标i
* @param j 数组角标j
*/
public static void swap(int[] arr, int i, int j)
{
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
/**
* 打印数组
* @param arr 数组
*/
public static void printArray(int[] arr)
{
for (int i = 0; i < arr.length; i++) {
if( i != arr.length -1)
{
System.out.print(arr[i] + ", ");
}
else
{
System.out.println(arr[i]);
}
}
}
【冒泡排序】:相邻的两个元素进行排序,如果符合条件换位。
【冒泡排序的特点】:
每次循环都是从0角标开始的。
第一圈最值出现在最后位。
【实例 1】:代码体现
public static void main(String[] args) {
int[] arr = {5,1,6,4,2,8,9};
System.out.println("-------------排序前-------------");
printArray(arr);
bubbleSort(arr); //冒泡排序
System.out.println("-------------排序后-------------");
printArray(arr);
}
/**
* 冒泡排序
* @param arr 数组
*/
public static void bubbleSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++) { //共循环比较arr.length - 1圈
for (int j = 0; j < arr.length - i -1; j++) { //-i:让每次比较的元素减少; -1:避免角标越界
if(arr[j] > arr[j + 1])
{
swap(arr, j, j + 1); //两数交换,但是直接换位置,性能低,一圈中要换好几次
}
}
}
}
/**
* 交换数组中的两个元素
* @param arr 数组
* @param i 数组角标i
* @param j 数组角标j
*/
public static void swap(int[] arr, int i, int j)
{
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
/**
* 打印数组
* @param arr 数组
*/
public static void printArray(int[] arr)
{
for (int i = 0; i < arr.length; i++) {
if( i != arr.length -1)
{
System.out.print(arr[i] + ", ");
}
else
{
System.out.println(arr[i]);
}
}
}
【实例 2】:冒泡排序的更优解(每一圈取出最值的角标,与最后的角标交换位置)
如果两个数需要换位置的话,先不要置换它们的位置,先把它们需要换位置的角标及元素记录下来。
在栈内存中,定义两个变量,临时记录下来,全都比较完后,取最终需要换位置的值就可以了。
把堆内存中频繁地换位置,转移到栈内存中。
public static void main(String[] args) {
int[] arr = {5,1,6,4,2,8,9};
System.out.println("-------------排序前-------------");
printArray(arr);
bubbleSort(arr); //冒泡排序
System.out.println("-------------排序后-------------");
printArray(arr);
}
/**
* 冒泡排序
* @param arr 数组
*/
public static void bubbleSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++) {
int j = 0, max = 0; //max 记录最大值的角标
for (j = 1; j < arr.length - i; j++) {
if(arr[j] > arr[max])
{
max = j;
}
}
if(j-1 != max) //如果max 角标不等于最后一个角标,两数交换
{
swap(arr, j-1, max); //两数交换
}
}
}
/**
* 交换数组中的两个元素
* @param arr 数组
* @param i 数组角标i
* @param j 数组角标j
*/
public static void swap(int[] arr, int i, int j)
{
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
/**
* 打印数组
* @param arr 数组
*/
public static void printArray(int[] arr)
{
for (int i = 0; i < arr.length; i++) {
if( i != arr.length -1)
{
System.out.print(arr[i] + ", ");
}
else
{
System.out.println(arr[i]);
}
}
}
【插入排序】:将一个元素插入到已经排好序的数组中去,从而得到一个新的数组长度 + 1 的有序数组。
【插入排序的原理】:
假设数组为一个有序数组。
每次判断数组元素是否大于前一个数,如果小于的话,就往前遍历数组,将大于这个数的元素往后移,将这个数放入到具体位置,使数组依然为有序数组。
【实例】:代码体现(针对 insterSort 方法)
/**
* 直接插入排序
* @param arr 数组
*/
public static void insterSort(int[] arr)
{
for (int i = 1; i < arr.length; i++) { //由角标1开始遍历数组
if(arr[i] < arr[i-1]) //判断数组元素是否大于前一个数
{
int temp = arr[i], j;
for (j = i - 1; j >= 0 && arr[j] > temp; j--) { //数组往后移动
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
}
}
【希尔排序】:把数组进行分组,分割成若干个子序列,然后对子序列进行基本的插入排序。
【希尔排序的原理】:
【实例 】:代码体现
/**
* 直接插入排序 + 希尔排序 + space取整
* @param arr 数组
*/
public static void shellSort(int[] arr)
{
int space = arr.length;
do
{
space >>= 1; // space = space/2;
for (int i = space; i < arr.length; i++) { //在直接排序的基础上,将间隔1,改为space
if(arr[i] < arr[i-space])
{
int temp = arr[i], j;
for (j = i - space; j >= 0 && arr[j] > temp; j -= space) {
arr[j + space] = arr[j];
}
arr[j + space] = temp;
}
}
}while(space > 0);
}
数组的查找操作:在遍历中进行判断。
【查找数组的方法】:
简单查找:直接遍历数组进行匹配
折半查找:折中找中间值,提高效率,但必须要保证该数组是有序的数组。
【实例】 定义功能,获取key第一次出现在数组中的位置,如果返回是-1,那么代表该key在数组中不存在
(因为数组下标不会存在-1)
/**
* 获取key第一次出现在数组中的位置
* @param arr 数组
* @param key 匹配的数据
* @return 数组角标或-1,如果为-1,没有找到key
*/
public static int getIndex(int[] arr, int key)
{
for (int i = 0; i < arr.length; i++) {
if(key == arr[i])
{
return i;
}
}
return -1;
}
/**
* 获取key第一次出现在数组中的位置
* @param arr 数组
* @param key 匹配的数
* @return 数组角标或 -1
*/
public static int halfSearch(int[] arr, int key)
{
int min = 0, max = arr.length - 1, mid;
while(min <= max) //当min>max, 循环结束
{
mid = (min + max) >> 1; //获得mid
if(key > arr[mid])
{
min = mid + 1;
}
else if(key < arr[mid])
{
max = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
【实例】 有个有序的数组,想要将一个元素插入到该数组中,还要保证该数组是有序的。
【思路】:通过折半的形式去查找这个数,在数组中的位置,如果这个数存在,在这个位置上把这个数插入,如果不存在,返回最小角标的值,就可以得到要插入的位置。
/**
* 折半查找,获取插入元素的角标
* @param arr 数组
* @param num 要插入的元素
* @return 要插入元素的角标
*/
public static int halfSearch(int[] arr, int num)
{
int min = 0, max = arr.length - 1, mid;
while(min <= max)
{
mid = (min + max) >> 1;
if(num > arr[mid])
{
min = mid + 1;
}
else if(num < arr[mid])
{
max = mid - 1;
}
else
{
return mid;
}
}
return min;
}
/**
* 将元素插入的相应的位置上,获得新数组
* @param arr 数组
* @param index 要插入的角标
* @param num 元素值
* @return 新数组
*/
public static int[] getNewArray(int[] arr, int index, int num)
{
int[] rtnArr = new int[arr.length + 1];
for (int i = 0; i < index; i++) {
rtnArr[i] = arr[i];
}
rtnArr[index] = num; //插入元素到指定位置
for (int i = index; i < arr.length; i++) {
rtnArr[i + 1] = arr[i];
}
return rtnArr;
}
/**
* 十进制转换二进制
* @param num 十进制数
* @return 二进制(以字符串形式返回)
*/
public static String toBin(int num)
{
StringBuilder sb = new StringBuilder();
while(num > 0) //有局限,负数无法求出
{
sb.append(num % 2 );
num >>= 1;
}
return sb.reverse().toString();
}
【查表法】:将所有的元素临时存储起来,建立对应关系,每一次&运算后的值作为索引去查建立好的表,就可以找到对应的元素(不用强制转换了)
【利用查表法,更改上面的十进制转换】
/**
* 进制表
*/
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f'
};
/**
* 十进制转换二进制
* @param num 十进制数
* @return 二进制(以字符串形式返回)
*/
public static String toBin(int num)
{
StringBuilder sb = new StringBuilder();
while(num != 0)
{
int temp = num & 1;
sb.append(digits[temp]); //利用查表法,直接获取对应值
num >>>= 1;
}
return sb.reverse().toString();
}
/**
* 进制表
*/
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f'
};
/**
* 十进制转换二进制
* @param num 十进制数
* @return 二进制(以字符串形式返回)
*/
public static String toHex(int num)
{
StringBuilder sb = new StringBuilder();
while(num != 0)
{
int temp = num & 15;
/*if(temp > 9)
{
sb.append((char)(temp-10 + 'a'));
}
else
{
sb.append(temp);
}*/
sb.append(digits[temp]);
num >>>= 4;
}
return sb.reverse().toString();
}
/**
* 进制表
*/
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f'
};
public static void main(String[] args) {
System.out.println(toBin(0));
System.out.println(toHex(-60));
}
/**
* 十进制转各种进制数
* @param num 十进制数
* @param base &运算的基数
* @param offset >>>位移多少位
* @return
*/
public static String trans(int num, int base, int offset)
{
if(num == 0) // 不然当0的时候,没有值
{
return "0";
}
StringBuilder sb = new StringBuilder();
while(num != 0)
{
int temp = num & base;
sb.append(digits[temp]);
num >>>= offset;
}
return sb.reverse().toString();
}
/**
* 十进制转换二进制
* @param num 十进制数
* @return 二进制(以字符串形式返回)
*/
public static String toBin(int num)
{
return trans(num, 1, 1);
}
/**
* 十进制转换十六进制
* @param num 十进制数
* @return 十六进制(以字符串形式返回)
*/
public static String toHex(int num)
{
return trans(num, 15, 4);
}
/**
* 十进制转换八进制
* @param num 十进制数
* @return 八进制(以字符串形式返回)
*/
public static String toOctal(int num)
{
return trans(num, 7, 3);
}
数组中的数组:往数组中存放数组,把数组作为元素存入另一个数组中。
【一维数组】: | int[ ] arr = new int[3]; |
【二维数组】: |
【数组的格式 1】 int[ ] arr = new int[3][4];
|
【数组的格式 2】 int[ ] arr = new int[3][ ];
|
|
【数组的格式 3】(静态存储方式) int[ ] arr = { {3, 5, 7}, {2, 3, 5}, {6, 1, 8, 2} };
|
|
【一维数组 与 二维数组的小知识】: 1. 一维数组:int[ ] x = int x[ ] 2. 二维数组:int[ ][ ] x = int x[ ][ ] = int[ ] x[ ] 【实例】int[ ] x, y[ ]; => int[ ] x, int[ ] y[ ]; 1. [ ] 定义在类型中,是随类型走的,而类型的[ ],变量都有效 2. [ ] 定义在变量后,是随着变量走的,只有单个变量有效。 (1)x[0] = y [错误] (2)y[0] = x [正确] (3)y[0][0] = x [错误] (4)x[0][0] = y [错误] (5)y[0][0] = x[0] [正确] (6)x = y [错误] |
【实例】打印二维数组地址值
int[][] arr = new int[3][4];
System.out.println(arr); //[[I@26a2bd15
System.out.println(arr[0]); //[I@62bc184
/*
二维数组的首地址:两个左中括号
一维数组的首地址:一个左中括号
*/
【分析】:
【实例】打印二维数组地址值
int[][] arr = new int[3][];
System.out.println(arr); //[[I@26a2bd15
System.out.println(arr[0]); //null (一维数组没有指定)
arr[0] = new int[3];
arr[1] = new int[1];
arr[2] = new int[2];
arr[0][2] = 90;
arr[1][1] = 89;
arr[2][2] = 78;
System.out.println(arr.length); //结果为:3
System.out.println(arr[0].length); //结果为:3 打印二维数组中第一个一维数组的长度