Java学习18:数组、Arrays工具类、一些算法

一维数组

Java语言中的数组是一种引用数据类型。不属于基本数据类型。数组的父类是Object。

数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)

数组当中可以存储基本数据类型的数据,也可以存储引用数据类型的数据。

数组因为是引用类型,所以数组对象是堆内存当中。(数组是存储在堆当中的)

数组当中如果存储的是“java对象”的话,实际上存储的是对象的“引用(内存地址)”。

数组一旦创建,在java中规定,长度不可变。(数组长度不可变)

数组的分类:一维数组(常用)、二维数组(很少)、三维数组、多维数组......

所有的数组对象都有length属性(java自带的),用来获取数组中元素的个数。

java中的数组要求数组中的元素类型统一。比如int类型的数组只能存储int类型。

数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有队则的挨着排列的)是连续的。内存地址连续。这是数组存储元素的特点(特色)。数组实际上是一种简单的数据结构。

所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。(数组中首元素的内存地址作为整个数组对象的内存地址。)

数组中每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标是:length-1。下标非常重要,因为我们对数组中元素进行“存取”的时候,都需要通过下标来进行。

数组这种数据结构的优点和缺点是什么?
优点:
查询/查找/检索某个下标上的元素时效率极高。可以说时查询效率最高的一个数据结构。
为什么检索效率高?
第一:每一个元素的内存地址在控件存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素的内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。

数组中存储100个元素,或者存储100万个元素,在元素查询/检索方面,效率是相同的,因为数组中元素查找到时候不会一个一个找,是通过数学表达式计算出来的。(算出一个内存地址,直接定位的。)

缺点:
第一:由于为了保证数组中每个元素的内存地址连续,所以在数组上随即删除或者增加元素的时候,效率较低,因为随机增删元素会设计到后面元素统一向前或向后位移的操作。
第二:数组不能存储大数据量,因为很难在内存控件上找到一块特别大的连续的内存空间。

数组的内存结构.png

注意:对于数组中最后一个元素的增删,是没有效率影响的。

怎么声明/定义一个一维数组?
语法格式:
int[] array1;
double[] array2;
boolean[] array3;
String[] array4;
Object[] array5;

怎么初始化一个一维数组呢?
包括两种方式:静态初始化一维数组,动态初始化一维数组。
静态初始化语法格式:
int[] array = {100,200,300,55};
动态初始化语法格式:
int[] array = new int[5];//这里的5表示数组的元素个数。
初始化一个5个长度的int类型数组,每个元素默认值0。
String类型的,默认值是null。

访问数组中的元素,变量名[]
取(读)
第一个元素:变量名[0]
最后一个元素:变量名[变量名.length - 1]
存(改)
把第一个元素改为111:变量名[0] = 111;
把最后一个元素改为0: 变量名[变量名.length - 1] = 0;

怎么遍历一维数组呢?
for(int i = 0; i < 变量名.length; i++){
System.out.println(变量名[i]);
}

数组下标越界会出现:ArrayIndexOutOfBoundsException

如果从最后一个元素遍历到第一个元素
for(int i = 变量名.length - 1 ; i >= 0 ; i--)
System.out.println(变量名[i]);
}

当你创建数组的时候,确定数组中存储哪些具体d 元素时,采用静态初始化方式。
当你创建数组的时候,不确定将来数组中存储哪些数据,你可以采用动态初始化的方式,预先分配内存空间。

如果直接传递一个静态数组的话,语法必须这样写:
new int[]{1,2,3}
不能直接把数组放进去

main方法上面的“String[] args”有什么用?
JVM负责调用main方法,JVM调用main方法的时候,会自动传一个String数组过来。
JVM默认传过来的数组对象的长度,默认是0。
这个数组时留给用户的,用户可以在控制台上输入参数,这个参数自动会被转换为“String[] args”

在java开发中,数组长度一旦确定不可变,那么数组满了怎么办?
数组满了,需要扩容。
java中对数组的扩容是:先新建一个大容量的数组,然后将小容量的数组中的数据一个一个拷贝到大数组当中。

数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝。
可以在创建数组对象的时候预估计一下多长合适,最好预估准确,这样可以减少数组的扩容次数。提高效率。

java中的数组是怎么进行拷贝的呢?
System.arraycopy();
5个参数:拷贝源,源起始下标,目标,目标下标,拷贝长度

二维数组

二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。

三维数组是一个特殊的二维数组,特殊在这个二维数组中每一个元素是一个一维数组。

二维数组的静态初始化
int[][] array = {{1,2,3},{4,5,6},{7,8,9}};

二维数组中元素的访问
变量名[二维数组中的一维数组的下标][一维数组的下标]
a[0][0]:表示第1个一维数组中的第1个元素。
a[3][100]:表示第4个一维数组中的第101个元素。

注意:对于 a[3][100]来说,其中a[3]是一个整体。[100]是前面a[3]执行结束的结果然后再下标100。

遍历二维数组

遍历二维数组.png

动态初始化二维数组
int[][] array = new int[3][4];

new int[][]{{1,2,3,4},{5,6,7,8},{9,10,15,111}}

Array工具类

工具类当中的方法都是静态的。

java.util.Arrays
Arrays是一个工具类,其中有一个sort()方法,可以排序,静态方法,直接使用类名调用就行。

主要使用的是两个方法:二分法查找、排序

java.util.Arrays;工具类中有哪些方法,我们开发到时候要参考API帮助文档,不要死记硬背

排序:Arrays.sort(arr);
二分法:Arrays.binarySearch(arr,参数);

算法

算法在以后的开发中我们不需要使用。因为java已经封装好了,直接调用就行。只不过以后面试的时候可能会有机会碰上。

冒泡排序算法

冒泡排序0.png
冒泡排序1.png
public class MaoPao {
    public static void main(String[] args) {
        int[] arr = {9,8,10,7,6,0,11};
        int count = 0;

        for (int i = arr.length - 1; i > 0; i--){
            for (int j = 0; j < i; j++){
                //不管是否需要交换位置,总之是要比较一次的。
                count++;
                if (arr[j] > arr[j+1]){
                    //交换位置
                    int temp;
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        
        //输出比较次数
        System.out.println("比较次数:" + count);
        //输出结果
        for (int i = 0; i < arr.length; i++){
            System.out.println(arr[i]);
        }
    }

}

选择排序算法

选择排序:每一次从这堆“参与比较的数据”当中找出最小值,拿着这个最小值和“参与比较的这堆最前面的元素”交换位置。

选择排序比冒泡排序好在:每一次的交换位置都是有意义的。

选择排序比冒泡排序的效率高。
高在交换位置的次数上。
选择排序的交换位置是有意义的。
循环一次,然后找出参加比较的这堆数据中最小的,拿着这个最小的值和最前面的数据交换位置。

选择排序.png
public class XuanZePaiXu {
    public static void main(String[] args) {
        //int[] arr = {3,1,6,2,5};
        int[] arr = {9,8,10,7,6,0,11};
        int count = 0;
        int count2 = 0;

        //选择排序
        //5条数据循环四次。(外循环四次)
        for (int i = 0;i < arr.length - 1; i++){
            //i的值是0,1,2,3
            //i正好是“参加比较的这堆数据中”最左边的那个元素的下标
            //i是一个参与比较的这堆数据中的起点下标。
            //假设起点i下标位置上的元素是最小的。
            int min = i;

            for (int j = i+1; j< arr.length;j++){
                count++;
                if (arr[j] < arr[min]){
                    min = j;//最小值的元素下标是j
                }
            }

            //当i和min相等时,表示最初猜测是对的。
            //当i和min不相等时,表示最初猜测是错的。有比这个元素更小的元素。
            //需要拿着这个更小的元素和最左边的元素交换位置。
            if (min != i){
                //表示存更小的数据
                //arr[min] 最小的数据
                //arr[i] 最前面的数据
                int temp;
                temp = arr[min];
                arr[min] = arr[i];
                arr[i] = temp;
                count2++;
            }
        }

        //冒泡排序和选择排序实际上比较的次数没变。
        //交换的次数减少了。
        System.out.println("比较次数:" + count);
        System.out.println("交换位置的次数:" + count2);

        //排序之后遍历
        for (int i = 0;i < arr.length; i++){
            System.out.println(arr[i]);
        }
    }
}

数组的元素查找

有两种方式:
第一种方式:一个一个挨着找,直到找到为止。
第二种方式:二分法查找(算法),这个效率较高。
第一种方式:

public class ChaZhao {
    public static void main(String[] args) {
        int[] arr = {4,5,6,87,8};
        //需求:找到87的下标。
        //一个一个挨着找
        /* for (int i = 0; i < arr.length; i++) {
            if (arr[i] == 87) {
                System.out.println("87元素下标为:" + i);
            }
        }
        //程序执行到此处,表示没有87
        System.out.println("87元素不存在!");
        */

        //最好将以上的程序封装一个方法,思考:传什么参数?返回什么值?
        //传什么:第一个参数时数组,第二个参数时被查找的元素。
        //返回值:返回被查找的这个元素的下标。如果找不到返回-1
        int index = arraySearch(arr,87);
        //可以先定义这个方法报红没事,光标放上alt + 回车,自动生成。
        System.out.println(index == -1 ? "该元素不存在" : "该元素下标是:" + index);
    }

    /*
    * 从数组中检索某个元素的下标(如果有相同的元素,返回的是第一个元素的下标)
    * @param arr 被检索的数组
    * @param ele 被检索的元素
    * @return 大于等于0的数表示元素的下标,-1表示该元素不存在。
    * */
    public static int arraySearch(int[] arr, int ele) {
        for (int i = 0; i < arr.length; i++) {
            if (ele == arr[i]) {
                return i;
            }
        }
        return -1;
    }
}

第二种方式:二分法查找(折半查找)
第一:二分法查找算法是基于排序的基础之上。(没有排序的数据是无法查找的。)
第二:二分法查找效率要高于一个挨着一个的这种查找方式。
第三:二分法查找原理


二分法查找.png

二分法查找到终止条件:一直折半,直到中间的哪个元素恰好是被查找的元素。

public class ErFenFa {
    public static void main(String[] args) {

        int[] arr = {100,200,230,600,1000,2000,9999};

        //找出这个数组中200所在的下标
        //调用方法
        int index = binarySearch(arr,200);
        System.out.println(index == -1 ? "该元素不存在" : "该元素下标为:" + index);
    }
    /*从数组中查找目标元素的下标。
    arr 被查找的数组(这个必须是已经排序的。)
    *dest 目标元素
    return -1 表示该元素不存在,其他表示返回该元素的下标
    * */

   public static int binarySearch(int[] arr, int dest) {
       //开始下标
       int begin = 0;
       //结束下标
       int end = arr.length - 1;
       //开始元素的下标只要在结束元素下标的左边,就有机会继续循环。
       while(begin <= end) {
           //中间元素下标
           int mid = (begin + end) / 2;
           if (arr[mid] == dest) {
               return mid;
           } else if (arr[mid] < dest) {
               //目标在中间的右边
               //开始元素下标需要发生变化(开始元素的下标需要重新赋值)
               begin = mid + 1;//一直增
           } else {
               //arr[mid] > dest
               //目标在中间的左边
               //修改结束元素的下标
               end = mid - 1;//一直减
           }
       }
        return -1;
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352