Java学习笔记系列-基础篇-数组

本文是Java学习笔记的基础篇,详细介绍了数组的声明方式、内存分配、基础运用,包括数组长度、索引、二维数组、拷贝、反转以及排序算法,如冒泡排序和快速排序。此外,还探讨了数组查询的线性查找和二分法查找,并通过实例讲解了如何实现两个数组的交集。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java学习笔记

Java学习笔记是一个持续更新的系列,工作多年,抽个空对自身知识做一个梳理和总结归纳,温故而知新,同时也希望能帮助到更多正在学习Java 的同学们。

本系列目录

入门篇

基础篇


数组

数组是储存数据类型相同的变量的集合,属于引用数据类型,自身也是堆中的一个对象。

声明方式

在Java中声明数组有两种方式,一是先声明数组长度,后赋值(动态初始化),二是声明数组时直接赋值,无需声明长度(静态初始化)。

实际操作时有三种方式:

  1. type[] 变量名 = new type[数组中元素的个数];
    比如:

    int[] a = new int[10];
    

    数组名,也即引用a,指向数组元素的首地址。
    推荐此种方式,更能表明数组类型

  2. type 变量名[] = new type[数组中元素的个数];
    如:

    int a[] = new int[10];
    

    这种方式和C语言一样

  3. 定义时直接初始化
    type[] 变量名 = new type[]{逗号分隔的初始化值};
    其中红色部分可省略,所以又有两种:

    int[] a = {1,2,3,4};
    int[] a = new int[]{1,2,3,4};
    

    其中int[] a = new int[]{1,2,3,4};的第二个方括号中不能加上数组长度,因为元素个数是由后面花括号的内容决定的。

内存分配

Java 语言是典型的静态语言, Java 数组也是静态的。

当数组被初始化之后,该数组所占的内存空间、数组长度都是不可变的。

由于数组属于引用对象,它从声明之后,就会保存在堆内存中,栈中只会保存指向堆中数组的地址。

例如

int a[] = new int[4];
a[0]=1;
a[1]=3;

在这里插入图片描述
Java 数组的变量对象并不是数组本身,它只是指向堆内存中的地址,它保存在栈中,上图左边为栈右边为堆。

基础运用

数组长度

Java中的每个数组都有一个名为length的属性,表示数组的长度。

length属性是public final int的,即length是只读的。
数组长度一旦确定,就不能改变大小。

int a[] = new int[10];
int n=a.length;//获取数组长度

数组索引

索引数组存储一系列经过组织的值,其中的每个值都可以通过使用一个无符号整数值进行访问。

数组索引始终从0开始,且添加到数组中的每个后续元素的索引以 1 为增量递增

索引的用法
数组通过[index]来获取指定位置的值,其中index为索引值

int a[] =  {1,2,3,4};
int index_1=a[0];//根据索引获取数组的数据
int index_2=a[1];//根据索引获取数组的数据
int index_3=a[2];//根据索引获取数组的数据
int index_4=a[3];//根据索引获取数组的数据

按照索引和数值的关系,如下表格

0123
1234

数组也可以配合循环语句进行业务处理

int a[] =  {1,2,3,4};
for(int i=0;i<a.length;i++){
	System.out.println(a[i]);//动态打印数组值
}
//也可以使用另一种循环方式
for(int temp:a){
	System.out.println(temp);//动态打印数组值
}
//还可以使用while和do-while,后续章节描述

结合索引与数值关系可以看出来,数组属于行数据,那么有行就有列,就可以构成表格数据格式,也就是二维数组。

二维数组

二维数组的每个元素都是一个一维数组,这些数组不一定都是等长的。

二维数组是数组的数组,一维数组可以看成行数据,那么二维数组就是表格形式,即每一行数据,都有多列值。

示例

int[][] i = new int[4][];
i[0]={1,2,3};
i[1]={1,3,4};
i[2]={1,4};
i[3]={1,5};
0123
1111
2345
34

二维数组定义方式和一维数组一样,也是两种方式

动态方式
二维数组可以在声明的时候就指定数组大小,然后通过索引进行动态数据填充。
如:

type[][] i = new type[2][];//推荐
type i[][] = new type[2][3];

声明二维数组的时候可以只指定第一维大小,空缺出第二维大小,之后再指定不同长度的数组。但是注意,第一维大小不能空缺(不能只指定列数不指定行数)。

静态方式
二维数组也可以在定义的时候初始化,使用花括号的嵌套完成,这时候不指定两个维数的大小,并且根据初始化值的个数不同,可以生成不同长度的数组元素。

如:

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

二维数据的循环

	int [] arr[] ={{1,2},{2,3,4},{3,4,5,6}};
	for(int i=0;i<arr.length;i++){ //0,1,2
        for(int j=0;j<arr[i].length;j++){ //每一个一维数组的长度
            System.out.print(arr[i][j]+"\t");
        }
        System.out.println();
	}

数组拷贝

对数组的引用地址或者值进行复制。

数组属于引用对象,复制数组的引用地址,使两个数组指向同一个内存地址,因此修改一个数组时也会影响另一个。

拷贝数组的指针(引用地址)

 public static void main(String[] args) {
        int[] array1={11,22,33,44};
        int[] array2=new int[5];
        array2=array1;
        array1[0]=1;
        System.out.println(array2[0]);//两个数组指向同一内存,所以修改array1,即修改array2
    }

拷贝数组的值

 public static void main(String[] args) {
		int [] array1={11,22,33,44};//源数组
        int [] array2=new int[4];//目标数组,长度必须大于等于源数组,负责会报错
        for (int i = 0; i <array1.length ; i++) {
            array2[i]=array1[i];
        }
        //打印结果
        for(int temp:array2){
			System.out.println(temp);//动态打印数组值
		}
    }

arraycopy方法
Java提供了System.arraycopy()方法,用于将一个数组的元素快速拷贝到另一个数组。

 public static void main(String[] args) {
		int [] array1={11,22,33,44};//源数组
        int [] array2=new int[4];//目标数组,长度必须大于等于源数组,负责会报错
        //拷贝
        System.arraycopy(array1, 0, array2, 0, array1.length);
         //打印结果
        for(int temp:array2){
			System.out.println(temp);//动态打印数组值
		}
    }

arraycopy方法的参数详解

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

    表示源数组

  • srcPos

    表示源数组中拷贝元素的起始位置。

  • dest

    表示目标数组,目标数组的长度至少大于等于原数组。

  • destPos

    表示拷贝到目标数组的起始位置

  • length

    表示拷贝元素的个数

数组反转

对数组的数值进行反转

int [] array={11,22,33,44,55};//源数组
for(int i=0;i<array.length/2;i++){
    int n=array[i];//获取第i个元素
    array[i]=array[array.length-i-1];//赋予对应的尾部元素
    array[array.length-i-1]=n;//将尾部元素值替换为i位置的元素
}

for (int n:array) {
    System.out.println(n);//打印结果
}

数组的查询

线性查找

查询目标值需要逐个遍历元素进行比较。

int [] array={11,22,33,44,55};//源数组
int n=33;//查找是否存在33,
for(int i=0;i<array.length;i++){
   if(array[i]==n){
	 System.out.println(n);//打印结果
	 break;
	}
}
二分法查找

二分法要求数组必须有序,在有序的情况下,拿到中间值与目标值比较,然后根据结果大小再取剩下的半截数组重复以上步骤,直到找到目标为止。

int [] array={11,22,33,44,55,66,77,88};//源数组
int n=33;//查找是否存在33,
int begin=0;
int end=array.length-1;
while(begin<=end){
   int middle = (begin+end)/2;//取中间的
   if(n==array[middle]){//找到目标
		System.out.println("目标下标为"+middle);
		break;
	}else if(array[middle]>n){//大于目标,则取左侧半截数组
		end = middle-1;
	}else{
		begin=middle+1;////小于目标,则取右侧半截数组
	}
}

数组的排序算法

将数组中的元素,按照逻辑顺序重新排列的方式称为排序,数组进行排序通常是为了快速查找目标。

排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。

十大排序算法

排序算法不限制计算机语言

计算方式算法名称
选择排序直接选择排序
选择排序堆排序
交换排序冒泡排序
交换排序快速排序
插入排序直接插入排序
插入排序拆半插入排序
插入排序Shell排序
归并排序归并排序
桶式排序桶式排序
基数排序基数排序

除了以上十种,还有其他排序算法,一般根据三个维度来衡量其优劣

  1. 时间复杂度
    分析关键字的比较次数和记录移动次数
  2. 空间复杂度
    分析排序算法中需要多少辅助内存
  3. 稳定性
    若记录A与记录B的关键字值相等,排序后A与B的先后次序保持不变,则称这种排序算法是稳定的

按照计算机情况可分为两类,即内部排序和外部排序。

内部排序:整个排序过程不需要借助于外部存储器,所有操作都在内存完成。
外部排序:参与排序的数据非常多,计算量很大,需要借助外设来参与存储。

冒泡排序

冒泡排序属于交换排序,交换排序主要是通过表中两个记录的比较,不符号升序或者降序要求,则将两者交换,而冒泡排序是通过对待排序序列从前向后,依次比较相邻元素的值大小,发现逆序则交互,是排序值较大的元素逐渐从前往后移动。

int [] array={7,11,55,4,78,69,99,88};//源数组

for (int i = 0; i <array.length ; i++) {
     for (int j = 0; j < array.length- i - 1; j++) {
         if(array[j]>array[j+1]){//比较相邻两个值得大小,符合要求则互换位置
             int temp=array[j];
             array[j]=array[j+1];
             array[j+1]=temp;
         }
     }
 }

 for (int n:array ) {
     System.out.println(n);
 }
//输出结果
4
7
11
55
69
78
88
99
快速排序

属于比较算法中目前计算速度最快的算法,它是从数列中挑选一个”基准“元素,然后逐个对比,数组中其他元素比基准元素小的放在基准元素前面,比它大的放在后面,这个操作也可以称为分区操作,然后重复整个操作直到数组变得有序。

快速排序基于分治思想,它的时间平均复杂度很容易计算得到为O(NlogN)

	public static void main(String[] args) throws InterruptedException {
        int [] array={7,11,55,4,78,69,99,88};//源数组
        int len;
        if(array == null
                || (len = array.length) == 0
                || len == 1) {
            return ;
        }
        sort(array, 0, len - 1);//快速排序核心算法
        for (int n:array) {
            System.out.println(n);
        }
    }

    /**
     * 快排核心算法,递归实现
     * @param array
     * @param left
     * @param right
     */
    public static void sort(int[] array, int left, int right) {
        if(left > right) {
            return;
        }
        // base中存放基准数
        int base = array[left];
        int i = left, j = right;
        while(i != j) {
            // 顺序很重要,先从右边开始往左找,直到找到比base值小的数
            while(array[j] >= base && i < j) {
                j--;
            }

            // 再从左往右边找,直到找到比base值大的数
            while(array[i] <= base && i < j) {
                i++;
            }

            // 上面的循环结束表示找到了位置或者(i>=j)了,交换两个数在数组中的位置
            if(i < j) {
                int tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }

        // 将基准数放到中间的位置(基准数归位)
        array[left] = array[i];
        array[i] = base;

        // 递归,继续向基准的左右两边执行和上面同样的操作
        // i的索引处为上面已确定好的基准值的位置,无需再处理
        sort(array, left, i - 1);
        sort(array, i + 1, right);

    }

其他数组问题

实现两个数组相交,返回结果符合样例(腾讯机试题)

import java.util.*;

public class Test {

//        样例1:
//        输入:
//        A = [1, 3, 8, 4, 6]
//        B = [3, 4, 9, 4]
//        输出:[3, 4]
//
//        样例2:
//        输入:
//        A = [1, 3, 8, 4, 6, 4]
//        B = [3, 4, 9]
//        输出:[3, 4]
//
//        样例3:
//        输入:
//        A = [1, 3, 8, 4, 6, 4]
//        B = [3, 4, 9, 4]
//        输出:[3, 4, 4]
    public static void main(String[] args) {
		
        int[] A = new int[]{1, 3, 8, 4, 6, 4, 9, 9};
        int[] B = new int[]{3, 4, 9, 4, 9};
        for (int n:toArr3(A, B)) {
            System.out.println(n);
        }
    }

    /***
     * 二分法
     * @param a
     * @param b
     * @return
     */
    public static int[] toArr3(int[] a, int[] b) {
        Arrays.sort(a);
        Arrays.sort(b);
        List<Integer> temp=new ArrayList<>();
        for(int i=0,j=0;i<a.length && j<b.length;) {
            if(a[i] == b[j]) {
                temp.add(a[i]);
                i++;
                j++;
            } else if(a[i] > b[j]) {
                j++;
            } else {
                i++;
            }
        }
        return temp.stream().mapToInt(Integer::valueOf).toArray();
    }

    /**
     * hashmap
     * @param a
     * @param b
     * @return
     */
    public static int[] toArr2(int[] a, int[] b) {
        Map<Integer,Integer> map=new HashMap<>();
        for (int i = 0; i < a.length ; i++) {
            Integer n=map.get(a[i]);
            map.put(a[i],n==null?1:++n);
        }
        List<Integer> temp=new ArrayList();
        for (int i = 0; i < b.length ; i++) {
            if(map.containsKey(b[i])){
                int j=map.get(b[i]);
                if(j>0){
                    temp.add(b[i]);
                    map.put(b[i],--j);
                }
            }
        }
        return temp.stream().mapToInt(Integer::valueOf).toArray();
    }

    /**
     * 标记数组
     * @param a
     * @param b
     * @return
     */
    public static int[] toArr(int[] a, int[] b) {
        List<Integer> temp=new ArrayList();
        boolean[] bl=new boolean[b.length];
        for(int i = 0; i< a.length; i++){
            int index= a[i];
            for (int j = 0; j < b.length; j++) {
                if(index== b[j]&&!bl[j]){
                    temp.add(index);
                    bl[j]=true;
                    break;
                }
            }
        }
        return temp.stream().mapToInt(Integer::valueOf).toArray();
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值