数组
- 1 数组的基本概念
- 1.1 数组的定义
- 1.2 数组的创建
- 1.3 数组的使用
- 1.3.1 获取数组元素
- 1.3.2 获取数组长度
- 1.3.3 数组的遍历
- 2 数组作为方法的参数
- 2.1 基本用法
- 3 数组练习
- 3.1 数组转字符串
- 3.2 数组拷贝
- 3.3 找数组中的最大元素
- 3.4 求平均值
- 3.5 查找数组中的指定元素
- 3.6 查找数组中指定元素(二分查找)
- 3.7 检查数组的有序性
- 3.8 冒泡排序
- 3.9 数组逆序
- 3.10 数组数字排列
1 数组的基本概念
1.1 数组的定义
数组,本质上就是,批量的创建一组相同类型的变量。
如果代码比较简单,只需要int a、int b两个数据就可以了,如果代码十分复杂需要创建一千个变量,我们可不能一个一个的 int 创建出来,这时候就需要 “ 规模制造 ”,用数组批量的一下子创建出来。注意,批量制造的一定得是***相同类型***。
1.2 数组的创建
三种数组的创建方式:
//1)数组的元素通过{}来指定
int[] arr1 = new int[]{1,2,3,4};
//2)数组的元素通过{}来指定
int[] arr2 = {1,2,3,4}; //一般写这样
//3)此时数组中的每个元素都被初始化成了0
// 这种写法主要用于指定长度
int[] arr3 = new int[4];
我们会发现,在C语言中,数组是int arr[4]={1,2,3,4}; 这样写的。在Java中,int 和 [ ] 写在一起,它们原则上来说,应该是一个整体,共同构成了变量的类型。
在C语言中,int[4] 和int[5] 是两种不同的类型,也就不能相互赋值,但是在Java中就没有这个问题,不管数组长度如何,是一个类型,就可以相 互赋值。
注意 一种错误写法:
中括号中数和大括号不能同时存在,以此避免数和大括号中的个数不相同。
int arr4 = new int[4]{1,2,3,4}; //编译出错
1.3 数组的使用
1.3.1 获取数组元素
和C语言类似,也是通过下标的方式来获取元素,不要忘记,大部分语言下标都是从 0 来时计算的,最后一个元素下标为length-1。使用下标访问的时候,不能超出有效范围,如果超出有效范围,在Java中会抛出一个数组下标越界异常。
1.3.2 获取数组长度
数组一旦创建好了之后,长度(length)就固定了,无法动态进行修改。通过arr1.length 来获取数组的长度。
System.out.println(arr1.length);
注意:
- 使用 arr.length 能够获取到数组的长度, 这个操作为成员访问操作符. 后面在面向对象中会经常用到。
- 使用 [ ] 按下标取数组元素。 需要注意,下标从 0 开始计数。
- 使用 [ ] 操作既能读取数据,也能修改数据。
- 下标访问操作不能超出有效范围 [0, length - 1] ,如果超出有效范围,会出现下标越界异常。
1.3.3 数组的遍历
第一种方法:
int[] arr = {1,2,3,4};
for (int i = 0; i < arr.length ; i++) {
System.out.println(arr[i]);
}
第二种方法,foreach方法,更简洁,但注意不能通过x 对数组进行修改:
for (int x : arr){
System.out.println(x);
}
2 数组作为方法的参数
2.1 基本用法
(1) 数组赋值:
int[] arr = {1,2,3,4};
int[] a = arr;
(2) 数组传参:
数组传参相当于 “ 赋值 ” 。赋值的时候,不是把原来的数组拷贝了一份形成新的数组,而是给原来的数组又起了个别名。数组是引用类型之一。
区分:内置类型在进行 = 的时候,不是起别名,而是创建了一个新的变量。
int[] arr = {1,2,3,4};
printArray(arr);
public static void printArray(int[] arr) {
for (int i = 0; i <arr.length ; i++) {
System.out.println(arr[i]);
}
通过别名修改数组,再从别的名字 “ 看 ” 都能看到
int[] arr = {1,2,3,4};
int[] a = arr;
transform(a);
printArray(arr);
public static void printArray(int[] arr) {
for (int i = 0; i <arr.length ; i++) {
System.out.println(arr[i]);
}
}
public static void transform(int[] a){
a[0] = 100;
}
(3) 写一个方法,把数组中的每个元素都 * 2
public static int[] transform2(int[] a){
for (int i = 0; i <a.length ; i++) {
a[i] = a[i] * 2;
}
return a;
}
这样写,输出的结果就是乘二的数组,但是因为对a 直接进行操作,也就是通过别名对数组本体进行了操作改变,就无法保留原数组了。怎么样保留原数组呢,我们可以再创建一个数组。
为了保留了原来的数组,new 了一个新的数组,然后对新数组进行乘二的操作,再返回这个新数组,就可以既保留原数组又得到一个乘二的新数组。
public static int[] transform2(int[] a){
int[] result = new int[a.length];
for (int i = 0; i <a.length ; i++) {
result[i] = a[i] * 2;
}
return result;
}
要注意,这个逻辑在C语言中是会出现问题的,在C 中result 在函数执行完之后会被释放,result 会变成野指针,再对它进行操作就有问题了,但是,在Java 中可以。这是因为它们的生命周期不一样,即变量 ” 什么时候生成 什么时候释放 “ 的问题。在Java 中new 出来的对象的生命周期,是根据垃圾回收器自动判定的。
3 数组练习
3.1 数组转字符串
需求:查看数组的内容,希望可以更方便的观察,就可以把数组的内容按照一定的格式组织成一个字符串。
(1)我们可以写一个方法,将数组转换成String ,注意这中间的加号 “ + “ 是String字符串和数字,因此它执行的是字符串的拼接而不是数字相加的运算。
public static String arrayToString(int[] arr){
String result = "[";
for (int i = 0; i < arr.length; i++) {
result += arr[i];
if(i != arr.length-1){
result +=",";
}
}
result += "]";
return result;
}
(2) 标准库中其实有这样的方法我们可以直接使用,在Arrays中有这种方法,而且它重载了很多种类型。
3.2 数组拷贝
(1) 我们可以写一个方法进行拷贝
public static int[] copyOf(int[] arr) {
int[] result = new int[arr.length];
for (int i = 0; i <arr.length ; i++) {
result[i] = arr[i];
}
return result;
}
(2) 同样也可以用标准库中的方法进行拷贝,我们可以看到,标准库的拷贝有两个参数,一个是数组名,一个是长度,如果新数组比原数组长度短,则相当于截断;如果新数组比原数组长度长,则长出来的部分自动用0 填充。
int[] result = Arrays.copyOf(arr,arr.length);
//拷贝某一部分
int[] result = Arrays.copyOfRange(arr, 2, 4);
System.out.println("result: " + Arrays.toString(result));
3.3 找数组中的最大元素
通过将一个位置固定当作 ” 擂台 “,然后其他的数来 ” 打擂台 “ 的方式进行比较,就是先固定一个位置,然后遍历其他的数与之比较大小,比它大就上去替代它,别的数再来比较,比擂台上的大就将擂台上的数 ” 打下擂台 “,大的数再站在擂台上,留到最后的就是最大的数。
public static int max(int[] arr) {
int result = arr[0];
for (int i = 0; i <arr.length ; i++) {
if(arr[i] > result){
result = arr[i];
arr[i] = result;
}
}
return result;
}
3.4 求平均值
即,将数组中的数全部加起来,然后处以数组的长度,来求得平均值,要注意平均值最后的返回值应该用double 类型,因为它不一定是整数。
public static double avg(int[] arr) {
int sum = 0;
for (int i = 0; i <arr.length ; i++) {
sum += i;
}
return (double) sum/ arr.length;
}
3.5 查找数组中的指定元素
给定值,查找下标,就是找到指定元素的位置。
public static int search(int[] arr, int toSearch) {
int index = 0;
for (index = 0; index <arr.length ; index++) {
if(toSearch == arr[index]){
return index;
}
}
System.out.println("找不到该数。");
return -1;
}
3.6 查找数组中指定元素(二分查找)
针对有序数组, 可以使用更高效的二分查找,即,先找到这组有序数组的中间值,然后比较要找的元素和中间值,如果比中间值小,则把中间后面的 ” 砍掉 “,再在前半段找中间值,判断要找元素与中间值的大小,重复上述操作直至找到制定元素;如果比中间值大,则把中间值前面的 ” 砍掉 “,在前半段找中间值,判断要找元素与中间值的大小,重复上述操作直至找到制定元素。
public static int binarySearch(int[] a, int toSearch) {
int left = 0;
int right = a.length-1;
while (left <= right ) {
int mid = left + right / 2;
if( toSearch > a[mid] ){
//在后半段查找
left = mid + 1;
}else if( toSearch < a[mid]){
//在左半段查找
right = mid -1;
}else{
// to
return mid;
}
}
System.out.println("找不到该数。");
return -1;
}
3.7 检查数组的有序性
找反例:
依次循环来找数组中的相邻元素,看相邻的两个元素是否符合升序要求,只要找到一个不符合的,反例就出来了,就可以认为不是有序数组;如果找完了所有的相邻元素,都没有找到反例,就是有序。
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;
}
3.8 冒泡排序
public static void bubbleSort(int[] arr) {
//从后往前遍历
//外层循环遍历的次数
//已排区间[0,bound)
//待排区间[bound,length)
for (int bound = 0; bound <arr.length ; bound++) {
//里层循环,比较交换
for (int cur = arr.length-1; cur >0 ; cur--) {
if(arr[cur - 1] > arr[cur]){
//不符合排序要求
int tmp = 0;
tmp = arr[cur - 1];
arr[cur - 1] = arr[cur];
arr[cur] = tmp;
}
}
}
}
3.9 数组逆序
逆序是要修改数组的内容,而不是反向打印(不修改内容)
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 --;
}
}
3.10 数组数字排列
从左往右,找到一个奇数;再从右往左,找到一个偶数;把这两个数交换,直到两个下标重合。
public static void transform2(int[] arr) {
int left = 0;
int right = arr.length-1;
while(left<right){
//先从左往右找到一个奇数
while(left<right && arr[left] % 2 ==0){
left++;
}
//从右往左找到一个偶数
while(left<right && arr[right] % 2 !=0){
right--;
}
//交换left和right
int tmp =arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}