1-5 Java数组

回顾

万年历

import java.util.Scanner;

/**
 * 万年历
 */
public class HomeWork {
    public static void main(String[] args) {
       //1.获取用户在控制台上输入的年月
        Scanner input = new Scanner(System.in);
        System.out.println("请输入年:");
        int year = input.nextInt();
        System.out.println("请输入月:");
        int month = input.nextInt();

       //2. 计算总天数
       int totalDays = getFormNowYearTo1900TotalDays(year) + getNowYearToDays(year,month);
       //3.求星期数【得到的值是0~6之间  0就是星期日 以此类推】
        int week = (totalDays+1)%7;
       //4.打印日历
       FormatCalendar(week,month,year);
    }

    /**
     * 格式化日历输出
     * @param week  月份第一天对应的星期
     * @param month 当前月份
     * @param year  当前年份
     */
    public static void FormatCalendar(int week,int month,int year){
        // 定义一个变量统计计数(逢7换行)
        int cut = 0;
        //打印表头
        System.out.println("星期日\t星期一\t星期二\t星期三\t星期四\t星期五\t星期六\t");
        //计算当前星期空行
        for(int i = 1 ;i<=week;i++){
            System.out.print("\t\t");
            cut ++ ; //计数
        }
        //打印月份对应的天数
        for(int i = 1;i<=getNowMonthDays(year,month);i++){
            //输出结果
            System.out.print(i+"\t\t");
            cut++;
            if(cut %7 ==0){
                System.out.println();//换行
            }
        }
    }

    /**
     * 判断闰年
     * @param year int类型数据 年份
     * @return  true 是闰年  false 不是
     */
    public static boolean isLeapYear(int year){
        return (year%4==0 && year%100 !=0 )||(year%400==0) ;
    }

    /**
     * 获取对应月份的天数
     * @param year  当前年份
     * @param month 当前月份
     * @return  月份对应天数
     */
    public static int getNowMonthDays(int year,int month){

        switch (month){
            case 2:
                //必须考虑闰年
               return isLeapYear(year) ? 29 : 28;
            case 4:
            case 6:
            case 9:
            case 11:
                return 30;
            default:
                return 31;
        }

    }

    /**
     * 计算当前年份到1900年的总天数
     * @param year  当前年份
     * @return  总天数
     */
    public static int getFormNowYearTo1900TotalDays(int year){
        // 定义一个变量计算总天数
        int sum = 0;
        for(int i = 1900; i<year;i++){
            //年对应天数不是闰年的366 就是 365
            sum += isLeapYear(i) ? 366 : 365;
        }
        return sum;
    }

    public static int getNowYearToDays(int year,int month){
        //定义一个变量存储总天数【距离当前年份1月份总天数】
        int totalDays = 0;
        for(int i = 1 ;i<month;i++){
            totalDays += getNowMonthDays(year,i);
        }
        return  totalDays;
    }
}

数组

PS:JVM内存的划分:认为根据不同内存空间的特点以及存储的数据来进行划分

程序计数器:当前线程所执行的字节码的行号指示器(执行到什么位置就添加行号)

**本地方法栈:**为了虚拟机使用native的方法的服务(在JavaAPI中修饰方法,让Java直接调用本机系统中C++或C语言来实现方法)

Java虚拟机栈(栈空间): 主要是用于描述Java方法执行的内存模型,每个方法执行的时候都会在栈中创建一个栈帧

​ 用于:存储局部变量,操作栈,方法出口等等(每一个方法,创建一个栈帧,栈帧存放了当前方法的数据信息(局部变量),当方法调用完毕之后,该方法的栈帧就会被销毁)

Java堆:被所有线程共享的一块内存区域,在虚拟机启动时候创建,所有的对象实例以及数组都要在对上分配空间(使用new关键字,就相当在堆中给开辟了一段新的内存空间)【引用类型】

PS:既然在堆中,我们就需要负责对创建空间回收,Java为了方便程序猿,提供了一套自己的垃圾回收机制【释放堆空间所创建对象】GC

Java方法区: 线程共享区域,存储着已被虚拟机加载的**【类信息,常量,静态变量】**

Java中数组

为什么要使用数组?

问题1:

声明变量时,每⼀个单独的变量都要对应⼀个变量名,但现在要处理⼀组相同类型的数据时,如要表示

班上100个⼈的年龄,绝对不希望定义100个变量来表示每个⼈的年龄,那怎么办呢?再看下列例⼦。

int age = 18;

int age2 = 19;

问题2:

求两个数之和,需要⼀个⽅法,求5个数之和,需要重载(方法名相同 参数列表不用(满足其一即可:顺序,个数,类型))⼀个⽅法,求100个数之和、1000个数之和、

10000个数之和,⽅法的参数列表会很⻓很⻓,⽽且⽅法得有很多个,⽽且还得去记住哪个⽅法是两个

参数的,哪个⽅法是三个参数的。这样总感觉很不爽,仔细分析这个功能,其实就是求⼀组数值的和⽽

已,这个⽅法并不在乎具体是多少个加数,它只在乎需要把哪些数加起来。

public static void add(int a,int b)

public static void add(int a,int b, int c ,int d,int e)

面临的问题:

  1. 大量相同数据类型数据的存储量
  2. 对存之后数进行操作(计算,修改,增加等等)

Java中就提供了一种解决方案,提供了一种数据结构,这个中数据结构可以满足大量相同数据类型的数据存储,也可以满足对当前数据的各种该操作【增,删,改,查】,这个数据结构就是数组

PS:因为还没有学习Object,所以现阶段数据只能存储一种数据类型,必须是相同数据类型的数据才能存储到对应的数组中

数组的特点:

​ 1.数组是按照一定顺序排列的同类型数据的集合

​ 2.数组中的存储内容被称为数组元素

​ 3.数组中存储元素的个数被称为数组的长度

PS:数组的长度可以接叫做**【数组的大小,数组中元素的个数】**

​ 我们学习的数组是一个定长容器,一但固定长度,长度是不可修改的【即存储元素个数】

​ 定长容器【即固定长度】,我们是不能动态修改数据数组大小

数组中可以存储什么样式对数据类型

数据类型存储的值数组中的默认值
byte整数0
short整数0
int整数0
long整数0
float小数或整数【小数需要添加f】0.0
double小数或整数0.0
char字符不可见的空字符‘\u0000’
booleantrue或falsefalse
引用类型(在堆中开辟空间)对象,数组,字符串,集合,接口等等null

PS:在创建数组的时候,没有进行任何赋值操作,数组是存在默认值,这个默认值取决于对应数据类型

数组的声明与创建

import java.util.Arrays;

/**
 * 数组的四种创建方式
 */
public class ArrayDemo1 {
    public static void main(String[] args) {
        /*
         1.标准方式
         数据类型[]  数组名 = new 数据类型[数组的长度];
         PS:
            1.数组使用是默认值进行的初始化
            2.数组的长度这-->必须是一个整数而且要大于0,决定着数组可以存储多少个数据
            3.new 是一个关键字 "以后看到使用new关键创建的 都是引用类型"
         */
        int[] arr = new int[10];
        System.out.println(Arrays.toString(arr));

        /*
        2. 先声明,在赋值
        数据类型[] 数组名;
        数组名 = new 数据类型[数组长度];
        PS:
          1.是先声明在赋值,在数据没有赋值之前不可以使用
          2.只有在赋值之后,才可以对数组进行操作
          3.这种创建方式也是使用默认值进行初始
         */
        char[]  arr2;
        //Variable 'arr2' might not have been initialized
        //System.out.println(Arrays.toString(arr2));
        arr2 = new char[10];
        System.out.println(Arrays.toString(arr2));


        /*
        3.是为了保留C语言的形式而留下的
        数据类型[] 数组名 = new 数据类型[]{元素1,元素2,....};
        PS:
            1.使用相对较少
            2.千万不要在常见数组的时候,指定数组的大小即长度
            3.数组长度是根据大括号中存储元素的个数来决定数组的长度
            4.这样创建数组是没有默认指定,存的值是由后面元素决定
         */
        float[] arr3 = new float[]{1.1f,1.2f,1.3f};
        System.out.println(Arrays.toString(arr3));

        /*
        4. 简化第三种
        数据类型[] 数组名 = {元素1,元素2,....}
        PS:
           1.使用相对较少
           2.不会使用new关键字,但是底层也是通过new关键字完成,并且也是存储在堆空间
           3.这个数组,长度是由元素个数决定,并且没有默认
         */
        boolean[] arr4 = {true,false,true};
        System.out.println(Arrays.toString(arr4));

    }
}

数据的存储过程

/**
 * 数组的存储过程
 */
public class ArrayDemo2 {
    public static void main(String[] args) {
        int[] arr = new  int[10];
        //1.直接打印数据的名字
        /*
         数组的名字是无法打印存储在数据中值的
         打印的结果是一个字符串[I@1b6d3586
         这个字符串的组成 [(代表数组)+I(数组中存储元素对数据类型)+@(拼接符无任何意义)+1b6d3586(十六机制是hashcode换算而来)
         因为这个字符串中的最会一个十六进制是hashcode换算而来,
         而hashcode是java中提供底层C++或C语言计算实际在内存中十六进制而转换成一个整数值
         
         所以我们就可以将当前的字符串看做是"数组的地址"
         */
        System.out.println(arr);
        //2.数组的数据类型和数组存储元素元素的数据类型
        /*
        数组的数据类型  --》 数据类型[]
        数据中元素的数据类型  --》  声明数组时的数据类型 
         */
        
        
        
    }
   
}


在这里插入图片描述

如何操作数组

数组的操作可以使用数组中提供第一个概念**【下标】**

PS:下标,角标,游标都是用同一个,这里我们就统称**“下标”**

import java.util.Arrays;

/**
 * 数组下标操作
 */
public class IndexArrayDemo {
    public static void main(String[] args) {
        /*
        数组下标:
        1.规定数组的下标是从【0】开始到【数组长度-1】结束
        0代表数组中第一个元素的位置, 数组长度-1代表着数组中最后一个元素的位置
         */
        //1.使用下标对数组指定位置进行赋值操作【修改指定位置的值】
        int[] arr = new int[10];
        //修改第5个位置的值
        //当前数组样式[0,0,0,0,0,0,0,0,0,0];
        //数组名[对应下标值] = 对应元素数据类型的值;
         arr[4] = 100;
        System.out.println(Arrays.toString(arr));
        
        //2.取值--》取值相当于是讲存储在数组中的元素获取出来,然后可以对这个值进行【判断,计算,给其他变量赋值】
        //PS:这个对应元素就是存在数组中的数据
        //取出第5个位置的值
        //数组名[对应下标值] --》 取出的数组数据就可以操作
        System.out.println(arr[4]);
    }
}

注意的问题

import java.util.Arrays;

/**
 * 数组下标操作问题
 */
public class IndexArrayDemo2 {
    public static void main(String[] args) {

        int[] arr = new int[10];

        //1.问题1:数组范围问题即数组到长度的值,不能是小于0或者大于等于数组长度
        //此时编译是不会报错,语法正确,只有在运行程序的时候会出现错误--》叫做异常
        //ArrayIndexOutOfBoundsException 数组下标越界异常 数组下标超出了范围【小于0 或 大于等于数组长度】
       // System.out.println(arr[-1]);
       // System.out.println(arr[10]);

        //2.某些情况下,我们是不知道数据的长度是多少的【双人开发传递参数】
        //获取数组的长度   --》   数组名.length
        System.out.println(arr.length);


    }
}


数组的遍历

数组的静态赋值和动态赋值

静态赋值:创建数组的同时进行赋值

动态赋值:先创建好数组,数组中数据通过读取文件,数据库,内存等等存储介质中来对数据继续赋值

普通for循环

语法:
    for(int i = 0;i<数组长度;i++){
        对数组的操作
    }
PS:
    1. int i = 0 因为数组的下标是从0开始的的,即是数组的第一个元素的位置【可以根据具体雪球动态改变初始值】
    2. i < 数组长度  为什么是数组长度,原因在于是从0开始的,并且当达到数组长度的时候 也就是循环停止的时候
                    即保证了数组下标的不会越界,也保证了可以取值到数组最后一个位置的值
                    【数组长度可以根据需求动态改变】
    综上所述:这个变量i值就是 数组中的 【下标】 对i值的操作就是对数组下标的操作 以此达到对数据中的元素进行操作
    /**
 * 遍历数组
 */
public class LoopArrayDemo {
    public static void main(String[] args) {
        //1.普通for循环
        int[] arr = new int[10];
        //普通for循环不仅可以赋值,而且可以取值
        for (int i = 0;i<arr.length;i++){
            //此时这个i就是下标,随着循环的变化,i的下标值也在变化
            arr[i] = (int)(Math.random()*100);
            System.out.println(arr[i]);
        }
    }
}

增强for循环【foreach循环】

foreach循环是Java5里面提供
PS:
foreach有两种形态:
1.面向数组  --》 底层是一个普通for循环,只不过将原有数据进行一次拷贝操作,操作是中变量,而非直接操作数组中的元素
2.面向集合  --》 底层是一个迭代器,操作原则完全遵守迭代器原则
    
语法:
    for(数组元素的数据类型 变量名 : 数组的名字){
     			操作这个变量【在for循环中声明变量】   
    }

import java.util.Arrays;

/**
 * 遍历数组
 */
public class LoopArrayDemo2 {
    public static void main(String[] args) {
        //1.增强for循环【foreach形式】
        int[] arr = new int[10];
        /*
        此时 a变量将循环得到 arr数组每一个元素值
        循环次数,就是数组的长度,只要获取完数组的最后一位 循环自然结束
        如果需要强行终止,此时需要if语句加以判断,辅助break关键字
         */
        for(int a : arr){
            //此时我们操作a变量,就相当于获取到了数组中元素
            //因为数组中没有进行任何赋值,所以是默认值0
            //当前声明这个循环变量,可以用来进行计算,判断,也可以给其他变量赋值
            System.out.println(a);
        }
        //以下操作方式不允许,原因查看反编译原码
        //通过增强for循环对数组进行赋值
        for(int a : arr){
            a = (int)(Math.random()*100);
        }
        System.out.println(Arrays.toString(arr));
        //此时通过反编译可以查看到
        /*
        //定义了一个int类型的arr数组,数组中元素个数是10个
        int arr[] = new int[10];
        //定义了一个ai数组,数组是引用于 arr这个数
        int ai[] = arr; //arr里面存储的地址给了ai,对ai的操作会直接影响arr

        i = ai.length; //同过 ai数组获取了数组长度,间接的等于 获取了原有arr数组的长度
        
        //此时使用的是一个普通for循环进行操作数组下标的遍历
        for (int k = 0; k < i; k++)
        {
          //我们在原码中使用增强for循环中定义a变量,在反编译原码中可以看到是一个声明
          //在循环体内的一个局部变量,并且和这个局部变量可以获取到数组每一个元素
            int a = ai[k];
         //在源码中对a变量操作,其实在底层中,只是对当前循环中局部变量a的操作
         //所以,增强for循环对变量修改并会影响原有数组中的值   
            a = (int)(Math.random() * 100D);
        }
        
         */
  

    }
}

总结:

​ 1.如果需要对数组即进行赋值也进行计算,建议可以使用普通for循环【操作下标】。

​ 2.如果是单纯打印,计算,或是判断查找等等,可以使用增强for循环【操作的是一个局部变量,这个变量不会影响数组原有的元素】。

数组的排序

数组排序是算法中一种,常见数排序有:【冒泡,选择、插入、快速、堆排序等等】,所有排序中效率最高的就是堆排序

PS: 在实际开发项目中,是很少让程序猿手写排序,除非对排序有硬性要求,否则可以使用工具类中所提供排序方法进行排序

​ 在排序时,如果没有明确的说明,默认所有排序都是升序【从小到大】 ,如果明确说明降序【从大到小】

冒泡排序:对未排序的数组中的各个元素从头到尾依次比较【比较的原则是两两相比(相邻远元素进行比较)】

PS:每一轮比较结束之后,都会有一个数放到争取的位置上【先找到最大值】

​ 以下的数据排序都已升序为例子

原始数据: 6 3 1 9 4
既然要对数组中每一个元素进行排序,每一个元素都需要和数组每一个元素进行比较,一个元素和所有的元素比较完毕之后就算是一轮
比较要持续执行多少轮,"数组的长度多少,就比较多少轮即可"    
    
原则两两相比,相邻比较【以第一轮为例】
6 3 1 9 4 --> 63 进行比较 如果前面的元素大于后面元素就交换位置    
3 6 1 9 4 --> 61 进行比较 如果前面的元素大于后面元素就交换位置 
3 1 6 9 4 --> 69 进行比较 如果前面的元素大于后面元素就交换位置,否不交换
3 1 6 9 4 --> 94 进行比较 如果前面的元素大于后面元素就交换位置
3 1 6 4 9 --> 9 和 ?已经没有元素了,所以比较自然停止    
---------------------------------------------------------------------
3 1 6 4 9 --> 31 进行比较 如果前面的元素大于后面元素就交换位置
1 3 6 4 9 --> 36 进行比较 如果前面的元素大于后面元素就交换位置,否不交换
1 3 6 4 9 --> 64 进行比较 如果前面的元素大于后面元素就交换位置  
1 3 4 6 9 --> 69 进行比较 如果前面的元素大于后面元素就交换位置,否不交换   
1 3 4 6 9 --> 9 和 ?已经没有元素了,所以比较自然停止     

代码实现:

import java.util.Arrays;

/**
 * 数组排序
 */
public class SortArrayDemo {
    public static void main(String[] args) {
        int[] arr = new  int[10];
        for(int i = 0;i<arr.length;i++){
            arr[i] = (int)(Math.random()*100);
        }
        System.out.println("没有排序之前数组中值是:"+ Arrays.toString(arr));
        //冒泡实现排序
        System.out.println("-------------------------------------------进行排序----------------------------------------------");

        //决定内层循环的次数即冒泡执行多少次
        for(int i = 0;i<arr.length;i++) {  //冒泡的比较次数有多少个数据就比较多少次,保证每一个数据就进行比较
            //冒泡排序的核心就是两两相比【相邻的两个元素进行比较】
            for (int j = 0; j < arr.length - 1-i; j++) {
                //第一个元素和第二个元素比,然后第二个和第三个元素比 以此类推
                if (arr[j] > arr[j + 1]) {
                    //交换
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
            System.out.println("第"+(i+1)+"次排序的结果是:"+Arrays.toString(arr));
        }
        System.out.println("-------------------------------------------------------------------------------------------------");
        System.out.println("排序之后数组中值是:"+ Arrays.toString(arr));
    }
}

PS:
    1.冒泡排序内层循环才是真正核心逻辑,即相邻的元素比较,而外层循环就是比较次数
    2.为什么在内层循环中需要添加-1-i逻辑
    PS:不减i值没有问题,不影响排序,但是不减1值会出现下标越界异常
    2.1 为什么-1?
        因为在内层循环中需要数组中相邻两个元素进行比较,此时需要使用内层循环变量控制当前数组元素位置即下标
        当前元素的下一位相当于是 【j+1】,如果for循环条件中只判断 j<arr.length,最后一次循环时 j+1值就会访问到数组的长度,
        就会出现数组下标越界异常,为了防止这个问题 ,所以需要在内层循环中进行-1操作。
    2.2 为什么-i?
        通过打印数组排序数据发现,每一轮比较都会将最大值放到数组的最后一位置【即每一轮比较都会有一个整数数据放到正确的位置】
        所有下一轮的比较就没有比较和上一轮已经确定的最大值进行比较,所以可以根据这个i值的自增来减少对数组中数据比较个数
        建议-i操作,就算不-i值也不会影响【不会影响排序结果,但是影响效率】
       
    如果需要使用冒泡排序进行升序,只需要在要if语句中使用【>】
    如果需要使用冒泡排序进行降序,只需要在要if语句中使用【<

选择排序:选择某个下标位置的元素,然后和后面的元素依次进行比较,若大于则交换,经过一轮排序之后,会找出最小值,放到数组的第一个位置,后续排序只要遵守这个原则即可

原始数据: 6 3 1 9 4
既然要对数组中每一个元素进行排序,每一个元素都需要和数组每一个元素进行比较,一个元素和所有的元素比较完毕之后就算是一轮
比较要持续执行多少轮,"数组的长度多少,就比较多少轮即可"    
    
固定一个位置,然后依次和后面的每一个元素进行比较【以第一轮为例】
6 3 1 9 4 --> 首先固定第一个元素的位置,即6这个位置,然后以6这个数据依次和后面的每一个元素进行比较
          --> 63 进行比较 如果前面的元素大于后面元素就交换位置
3 6 1 9 4 --> 因为是固定第一个元素的位置,所以不会进行移动,在因为63比较滞后进行交换 ,所以固定位置是3不是6--> 31 进行比较 如果前面的元素大于后面元素就交换位置
1 6 3 9 4 --> 因为是固定第一个元素的位置,所以不会进行移动,在因为31比较后进行交换 ,所以固定位置是1不是3--> 19 进行比较 如果前面的元素大于后面元素就交换位置,否则不换
1 6 3 9 4 --> 因为 19 比较并为发生交换,所以1值继续和后面元素进行较
          --> 14 进行比较 如果前面的元素大于后面元素就交换位置,否则不换
1 6 3 9 4    

PS:一轮比较下来一定会有一个数据放到正确的位置上,会先找出最小值

实现代码

 public static void main(String[] args) {
        int[] arr = new  int[10];
        for(int i = 0;i<arr.length;i++){
            arr[i] = (int)(Math.random()*100);
        }
        System.out.println("没有排序之前数组中值是:" + Arrays.toString(arr));
        //选择实现排序
        System.out.println("-------------------------------------------进行排序----------------------------------------------");
        //1.外层循环是整个数据比较排序的次数
        for(int i = 0 ;i<arr.length;i++){
            //2.内层循环是选择排序的核心逻辑【固定一个位置,依次比较后续的每一个元素】
            // i值就是固定位置【只有内层循环结束之后,外层循环的i值才会自增】
            // i值固定之后需要和i值之后的每一个元素进行比较, 使用内层循环变量j = i+1
            for(int j = i+1;j<arr.length;j++){
                if(arr[i] > arr[j]){
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println("排序之后数组中值是:" + Arrays.toString(arr));

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值