数组

数组的特点

Java语言中的数组不属于基本数据类型而是引用数据类型 , 所以数组对象是在堆内存当中存储的 , 数组的父类是Object

  • 数组是一个数据的集合 , 本质是一个容器可以存储“基本数据类型”或“引用数据类型”的数据 (不能直接存储java对象 ,存储的是“java对象”的内存地址 )
  • 数组对象都有length属性(java自带的 , 其他语言没有),用来获取数组中元素的个数 , 数组一旦创建它的长度不可变
  • 数组中每一个元素都有下标,下标从0开始以1递增。最后一个元素的下标是:length - 1 。我们对数组中元素进行“存取”的时候,都需要通过下标来进行
  • 如果数组的下标越界了 , 会出现 ArrayIndexOutOfBoundsException 数组下标越界异常

数组在内存方面存储的时候,数组中的元素内存地址是连续的 (有规则的挨着排列的)。数组都是拿首元素的内存地址作为整个数组对象的内存地址

数组中元素类型的默认值

数据类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
boolean false
char \u0000
引用数据类型 null

数组的优缺点

数组实际上是一种简单的数据结构 , 检索某个下标上的元素时效率极高( 数组中存储100个元素或存储100万个元素,在元素查询方面,效率是相同的)

  • 每一个元素的内存地址在空间存储上是连续的并且每一个元素类型相同,所以占用空间大小一样
  • 通过第一个元素的内存地址和每一个元素占用空间的大小,使用一个数学表达式就可以计算出某个下标处的内存地址然后通过内存地址定位该元素

为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低

  • 因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作 , 但是对于数组中最后一个元素的增删,是没有效率影响的
  • 数组的元素实际是不能删的,只能以覆盖的形式删除
  • 数组不能存储大数据量,因为很难在内存空间上找到一块特别大的连续的内存空间
  • 数组每次扩容的时候都要开辟一个新的数组并且还要将原先数组的内容拷贝到新数组(效率较低), 一般创建数组的时候事先预留一定大小的空间

一维数组

初始化一维数组的三种方式

数组和变量一样 ,可以声明的同时初始化 , 也可以先声明数组当满足一定条件的时候再初始化

  • **静态初始化: **int[] array = {}或int[] array = new int[]{} , int array[] = {} (这是C++风格,不建议在java中使用)
  • **动态初始化: **String[] names = new String[6] , 初始化6个长度的String类型数组,每个元素默认值null
  • int[] x,y[],x表示一维数组,y表示二维数组

什么时候采用静态初始化方式,什么时候使用动态初始化方式

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

我们可以使用 for 循环通过数组元素的下标访问数组的元素/值

  • 一般 i 的初始值为 0 , 因为数组元素的下标是从 0 开始编号
public class ArrayTest01 {public static void main(String[] args) {// 声明一个int类型的数组,使用静态初始化的方式int[] a = {1, 100, 10, 20, 55, 689};// 这是C++风格,不建议java中使用。int a[] = {1, 100, 10, 20, 689};// 所有的数组对象都有length属性System.out.println("数组中元素的个数" + a.length);// 数组中每一个元素都有下标 , 通过下标对数组中的元素进行存和取。// 读取第一个元素的值System.out.println("第一个元素 = " + a[0]);// 把第一个元素修改为111a[0] = 111;System.out.println("第一个元素 = " + a[0]);// 一维数组遍历for(int i = 0; i < a.length; i++){System.out.println(a[i]); }// 读取的第7个元素没有,出现ArrayIndexOutOfBoundsException异常//System.out.println(a[6]); // 初始化一个Object类型的数组,采用动态初始化方式 , 所以每个元素默认值是nullObject[] objs = new Object[3]; for (int i = 0; i < objs.length; i++) {System.out.println(objs[i]);}// 后期给objs数组中的元素赋值objs[0] = new Object();}
}

如果直接传递一个静态的一维数组的话,必须写成new int[]{1,2,3}的形式

public class ArrayTest04 {public static void main(String[] args) {// 静态初始化一维数组int[] a = {1,2,3};printArray(a);// 如果直接传递一个静态数组的话,语法必须这样写printArray(new int[]{1,2,3});// 动态初始化一维数组int[] a2 = new int[4];printArray(a2);printArray(new int[3]);}//这里相当于执行了 int[] array = new int[]{1,2,3}public static void printArray(int[] array){for (int i = 0; i < array.length; i++) {System.out.println(array[i]);}}
}

main方法接收用户输入参数

JVM调用main方法的时候,会自动传一个String数组过来

  • 这个数组对象不是null并且长度默认0 , 这个数组是用来接收用户运行程序时输入的参数的
  • JVM会自动将“abc def xyz”通过空格的方式进行分离,分离完成之后自动放到“String[] args”数组当中

运行程序时如何完成参数的传递

  • DOS窗口: java ArrayTest abc def xyz
  • IDEA : Run -> Edit Configurations ->Program arguments 中配置参数 abc def xyz

模拟一个系统,假设这个系统要使用,必须输入用户名和密码

public class ArrayTest06 {// 用户名和密码输入到String[] args数组当中public static void main(String[] args) {if(args.length != 2){System.out.println("使用该系统时请输入程序参数,参数中包括用户名和密码信息,例如:zhangsan 123");return;}// 程序执行到此处说明用户确实提供了用户名和密码。接下来你应该判断用户名和密码是否正确// 取出用户名String username = args[0];// 取出密码String password = args[1];// 假设用户名是admin,密码是123的时候表示登录成功。其它一律失败// 判断两个字符串是否相等,需要使用equals方法//if(username.equals("admin") && password.equals("123")){// 采用以下编码风格,及时username和password都是null,也不会出现空指针异常if("admin".equals(username) && "123".equals(password)){System.out.println("登录成功,欢迎[" + username + "]回来");System.out.println("您可以继续使用该系统....");}else{System.out.println("验证失败,用户名不存在或者密码错误!");}}
}

数组存储数据类型

在java中规定数组中存储的元素的类型要统一 , 即int类型数组只能存储int类型元素,Person类型数组只能存储Person类型或者Person类型的子类型的元素

  • 数组元素本身的运行类型还是它本身的类型 , 所以当调用子类特有的方法时需要向下转型
public class ArrayTest07 {public static void main(String[] args) {      // 动态初始化一个长度为2的Animal类型数组Animal[] ans = new Animal[2];// 创建一个Animal对象,放到数组的第一个盒子中ans[0] = new Animal();// Animal数组中只能存放Animal类型,不能存放Product类型//ans[1] = new Product();// Animal数组中可以存放Cat类型的数据,因为Cat是Animal的子类ans[1] = new Cat();// 创建一个Animal类型的数组,数组当中存储Cat和BirdAnimal[] anis = {new Cat(), new Bird()}; for (int i = 0; i < anis.length; i++){// 这个取出来的可能是Cat,也可能是Bird,不过肯定是一个Animal// 当调用的方法是父类中存在的方法时不需要向下转型。直接使用父类型引用调用即可Animal an = anis[i];an.move();// 当调用子对象特有方法的话,需要向下转型,因为父类没有该方法if(anis[i] instanceof Cat){Cat cat = (Cat)anis[i];cat.catchMouse();}else if(anis[i] instanceof Bird){Bird bird = (Bird)anis[i];bird.sing();}}}
}
// 动物类
class Animal{public void move(){System.out.println("Animal move...");}
}// 商品类
class Product{}// Cat是子类
class Cat extends Animal {public void move(){System.out.println("猫在走猫步!");}// 特有方法public void catchMouse(){System.out.println("猫抓老鼠!");}
}// Bird子类
class Bird extends Animal {public void move(){System.out.println("Bird Fly!!!");}// 特有的方法public void sing(){System.out.println("鸟儿在歌唱!!!");}
}

数组扩容(拷贝)

在java开发中,数组长度一旦确定不可变,所以数组满了就需要扩容。先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中

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

调用JDK System类中的 arraycopy(5个参数) 静态方法,来完成数组的拷贝

public class ArrayTest08 {public static void main(String[] args) {// 拷贝源(从这个数组中拷贝)int[] src = {1, 11, 22, 3, 4};// 拷贝目标(拷贝到这个目标数组上)int[] dest = new int[20]; //System.arraycopy(src, 1, dest, 3, 2);// 遍历目标数组for (int i = 0; i < dest.length; i++) {// 0 0 0 11 22 ... 0System.out.println(dest[i]); }// 数组中如果存储的元素是引用,也可以拷贝Object[] objs = {new Object(), new Object(), new Object()};Object[] newObjs = new Object[5];// 注意: 这里拷贝的对象的内存地址。对象在内存中还是只有一份System.arraycopy(objs, 0, newObjs, 0, objs.length);for (int i = 0; i < newObjs.length; i++) {System.out.println(newObjs[i]);}}
}

数组拷贝的时候 , 拷贝的是对象的内存地址, 对象在内存中还是只有一份

二维数组

二维数组的初始化

数组和变量一样 ,可以声明的同时初始化 , 也可以先声明数组当满足一定条件的时候再初始化, a[ 二维数组中的一维数组的下标 ] [ 一维数组中元素的下标 ]

  • **静态初始化: **int[] [] array = {{},{}} , int[] array[] = {{},{}} , int array[] [] = {{},{}}, new int[] []{{},{},{}}
  • **动态初始化:**int a[] [] = new int[2] [3] , 表示二维数组有2个一维数组 , 每个一维数组有3个元素
  • **动态初始化列数不确定: **类型[] [] 数组名 = new 类型[大小] [] , 因为二维数组保存的是一维数组的首元素内存地址 , 所以必须确定二维数组储存几个一维数组 , 至于这个一维数组有几个元素并不会影响一维数组首元素的内存地址

二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组 (一维数组的长度可以相同也可以不同)

数组和变量一样 ,可以声明的同时初始化 , 也可以先声明数组当满足一定条件的时候再初始化

public class ArrayTest10 {public static void main(String[] args) {// 静态初始化二维数组int[][] a = {{34,4,65},{100,200,3900,111},{0}};// 取出第2个一维数组当中第3个元素System.out.println("第二个一维数组中第三个元素:" + a[1][2]);// 取出第3个一维数组当中第1个元素System.out.println("第3个一维数组中第1个元素:" + a[2][0]);// 修改二维数组中元素的值a[2][0] = 11111;System.out.println(a[2][0]);// 注意别越界。否则报java.lang.ArrayIndexOutOfBoundsException异常//System.out.println(a[2][1]);// 动态初始化一个3行4列的二维数组。3个一维数组,每一个一维数组当中4个元素。int[][] array = new int[3][4];//创建有三个空间的二维数组,空间还没有开数据空间保存的都是nullint[][] arr = new int[3][];//遍历二维数组 arrfor(int i = 0; i < arr.length; i++) {//输出 arr 的每个一维数组for(int j = 0; j < arr[i].length; j++) {System.out.print(arr[i][j] + " ");}System.out.println();//换行}}
}

如果直接传递一个静态的二维数组的话,必须写成new int[] []{{},{},{}}

 public static void main(String[] args) {// 静态初始化int[][] a = {{1,2,3,4},{4,5,6,76},{1,23,4}};printArray(a);// 没有这种语法//printArray({{1,2,3,4},{4,5,6,76},{1,23,4}});// 必须写成new int[] [的形式]{{},{},{}}的形式printArray(new int[][]{{1,2,3,4},{4,5,6,76},{1,23,4}});}public static void printArray(int[][] array){// 遍历二维数组。for (int i = 0; i < array.length; i++) {for (int j = 0; j < array[i].length; j++) {System.out.print(array[i][j] + " ");}System.out.println();}}

二维数组的遍历

外层 for 循环遍历控制二维数组下标 , 内层 for循环控制一维数组下标 , 每遍历一个一维数组输出换行符

public class ArrayTest11 {public static void main(String[] args) {// 二维数组String[][] array = {{"java", "oracle", "c++", "python", "c#"},{"张三", "李四", "王五"},{"lucy", "jack", "rose"}};// 二维数组的遍历for(int i = 0; i < array.length; i++){ // 外层循环3次(负责纵向)// 负责遍历一维数组for(int j = 0; j < array[i].length; j++){System.out.print(array[i][j] + " ");}            // 每遍历一个一维数组输出换行符System.out.println();}}
}

java.util.Arrays

常用方法

Arrays里面包含了一系列的静态方法 , 用于管理或操作数组(比如排序,搜索 , 遍历)

方法名 功能
static String toString(任意类型的数组) 返回指定数组内容的字符串表示形式。字符串表示形式由数组的元素列表组成数组并没有重写Object的toString方法 , 默认输出还是对象的内存地址
void sort(任意类型的数组) 对指定的数组进行排序 , 默认按照从小到大的顺序排序
void sort(任意类型的数组, Comparator c) 传入一个实现了Comparator接口的匿名内部类(实现 compare方法) , 实现数组中元素的定制排序 , 创建比较器时可以指定泛型,避免向下转型
int binarySearch(数组 , 要查找元素) 使用二分查找的方式查找数组中某个元素的下标 , 要求该数组是升序排列的 , 所以可以先 sort 再查找 , 否则返回的数字都没有意义 , 若找不到元素则返回 -(该元素在数组中的下标 + 1) , 也可能是 -1
copyOf(数组, 拷贝的长度) 从指定数组中,拷贝指定个数组元素到新数组中 ,拷贝长度不能小于0, 新数组的数据类型和要拷贝的数组的数据类型一致 , 该方法的底层使用的是 System.arraycopy()
void fill(要填充的元素 , 数组) 使用某个元素去填充数组,可以理解成将该元素替换数组中原来所有的元素 , 可以快速的修改数组的默认值
boolean equals(要比较的数组) 比较两个数组元素内容是否完全一致,一样返回true否则返回false
List asList(一组数据以逗号隔开) 将一组数据转成一个List集合 , toArraay()表示将集合转化为数组
public class ArraysMethod01 {public static void main(String[] args) {       //使用sort方法将数组按照默认规则排序(从小到大)int[] arr = {112,3,4,56,67,1};Arrays.sort(arr);int[] arr = { 1, 2, 3, 4, 5 };  // 直接输出为内存地址[I@139a55System.out.println(arr.toString());  //使用toString方法返回数组内容的字符串表示形式System.out.println(Arrays.toString(arr));//使用sort方法将数组按照定制规则排序Integer array[] = {1, -1, 7, 0, 89};Arrays.sort(arr, new Comparator(){@Overridepublic int compare(Object o1, Object o2) {Integer i1 = (Integer) o1;Integer i2 = (Integer) o2;return i2 - i1;}});//输出排序后的数组中的内容System.out.println(Arrays.toString(array));//测试binarySearch方法Integer[] arr = {1, 2, 90, 123, 567};// 如果数组中不存在该元素,就返回 return -(low + 1); int index = Arrays.binarySearch(arr, 567);System.out.println("index=" + index);//测试copyOf方法Integer[] newArr = Arrays.copyOf(arr, arr.length);System.out.println(Arrays.toString(newArr));//测试fill方法 Integer[] num = new Integer[]{9,3,2};//使用99去填充num数组,可以理解成是替换数组中原来所有的元素Arrays.fill(num, 99);System.out.println(Arrays.toString(num));//测试equals方法Integer[] arr2 = {1, 2, 90, 123};//比较两个数组元素内容是否完全一致,一样返回true否则返回falseboolean equals = Arrays.equals(arr, arr2);System.out.println("equals=" + equals);//false//测试asList方法//将一组值转成一个List集合,asList编译类型是List(接口)List asList = Arrays.asList(2,3,4,5,6,1);System.out.println("asList=" + asList);// asList 运行类型 java.util.Arrays#ArrayList, 是Arrays类的静态内部类System.out.println("asList的运行类型" + asList.getClass());}
}

sort方法实现定制排序的原理

执行 TimeSort类binarySort方法( 二叉树排序) ,每次循环从待排序的数组中取出两个元素,根据动态绑定机制执行我们传入的匿名内部类实现的 compare () 方法接收这两个元素 , 方法结束后会返回一个值用来说明如何对这两个元素排序

  • 注意: 实现的 compare 方法的返回值是 int 类型 , 如果我们自定义规则时返回值不是 int 类型 , 我们需要自己指定什么情况下返回 int 类型
// binarySort方法的代码
while (left < right) {int mid = (left + right) >>> 1;//根据动态绑定机制,执行我们传入的匿名内部类实现的 compare () 方法//pivot, a[mid]都是数组中元素的值if (c.compare(pivot, a[mid]) < 0)right = mid;elseleft = mid + 1;
}// 我们传入的匿名内部类实现的 compare () 方法
new Comparator() {@Override//o1和o2是按照二叉排序的方式从数组中取出的两个元素public int compare(Object o1, Object o2) {Integer i1 = (Integer) o1;Integer i2 = (Integer) o2;//指定数组中的元素按照从大到小排序return i2 - i1;}
}

使用冒泡排序 + 匿名内部类实现的 compare () 方法仿照Arrays类的sort方法实现定制排序

public class ArraysSortCustom {public static void main(String[] args) {int[] arr = {1, -1, 8, 0, 20};bubbleSort(arr, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {int i1 = (Integer) o1;int i2 = (Integer) o2;return i2 - i1;}});System.out.println(Arrays.toString(arr));}//使用冒泡排序 + 匿名内部类实现的 compare () 方法实现定制排序public static void bubbleSort(int[] arr, Comparator c) {int temp = 0;for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {// 使用冒泡排序的方式从数组中取出元素,然后根据compare方法的返回值决定冒泡排序的规则if (c.compare(arr[j], arr[j + 1]) > 0) {temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}
}

自定义Book类,里面包含name和price,按书名的长度排序(从大到小)

class Book {private String name;private double price;public Book(String name, double price) {this.name = name;this.price = price;}//get,set,toString方法...
}
public class ArrayExercise {public static void main(String[] args) {Book[] books = new Book[4];books[0] = new Book("红楼梦", 100);books[1] = new Book("金瓶梅新", 90);books[2] = new Book("青年文摘20年", 5);books[3] = new Book("java从入门到放弃~", 300);//调用sort方法按照书名的长度来进行排序Arrays.sort(books, new Comparator() {//o1和o2是按照二叉排序的方式从数组中取出的元素,它们是Book类型的对象@Overridepublic int compare(Object o1, Object o2) {Book book1 = (Book) o1;Book book2 = (Book) o2;//自定义排序规则return book2.getName().length() - book1.getName().length();//按照price排序从大到小的方式自定义排序,从小到大同理double priceVal = book2.getPrice() - book1.getPrice();//如果发现返回结果和我们输出的不一致,就修改一下返回的 1 和 -1if(priceVal > 0) {return  -1;} else  if(priceVal < 0) {return 1;} else {return 0;}}});System.out.println(Arrays.toString(books));}
}

源码分析

Arrays的静态方法toString源码

public static String toString(int[] a) {// 内容不能为空如果为空的话就返回字符"null"if (a == null)return "null";// 定义一个数组的结束位置,方便我们可以进行判断int iMax = a.length - 1;// 长度是否为正数if (iMax == -1)return "[]";//使用StringBuilder进行字符串的拼接,提高效率StringBuilder b = new StringBuilder();b.append('[');for (int i = 0; ; i++) {b.append(a[i]);if (i == iMax)return b.append(']').toString();b.append(", ");}}

binarySearch源码

private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {int low = fromIndex;int high = toIndex - 1;while (low <= high) {int mid = (low + high) >>> 1;int midVal = a[mid];if (midVal < key)low = mid + 1;else if (midVal > key)high = mid - 1;else// key foundreturn mid; }// key not foundreturn -(low + 1);  }

Java中的数组和Arrays工具类相关推荐

  1. java 中 针对数组进行的工具类

    1.遍历数组的方法: public static void printfArray(int[] arr)  2. 获取数组中最大值: public static int getMax(int[] ar ...

  2. java基础语法-day24-25进阶-Arrays工具类String自动装箱自动拆箱

    p576 Arrays工具类 p577 冒泡排序 import java.util.Arrays;public class BubbleSort {/*** 冒泡排序:*/public static ...

  3. Java程序员的日常—— Arrays工具类的使用

    这个类在日常的开发中,还是非常常用的.今天就总结一下Arrays工具类的常用方法.最常用的就是asList,sort,toStream,equals,copyOf了.另外可以深入学习下Arrays的排 ...

  4. 关于Java中何时使用static和工具类相关知识

    2019独角兽企业重金招聘Python工程师标准>>> 一.使用static修饰变量:当对象中出现共享数据时,该数据被静态修饰,对象中的特有数据要定义成非静态存放于堆内存中. 二.使 ...

  5. Java中的RSA加解密工具类:RSAUtils

    本人手写已测试,大家可以参考使用 package com.mirana.frame.utils.encrypt;import com.mirana.frame.utils.log.LogUtils; ...

  6. Java中Date日期时间的工具类

    package me.xueyao.date;import java.sql.Timestamp; import java.text.ParseException; import java.text. ...

  7. JAVA中MD5加密(MD5工具类)

    转自:https://blog.csdn.net/starry7953810/article/details/79924156 为什么只有加密,没有解密呢?欢迎大佬留言解答 package utilw ...

  8. (后端)Java中关于金额大小写的工具类

    /*** 金额小数转换成中文大写金额* * @author Neil Han* */private static final String UNIT[] = { "万", &quo ...

  9. java中金额元转万元工具类

    public static void main(String[] args) {     // 具体的金额(单位元)     String value = "88000067898" ...

最新文章

  1. 进击的 Java ,云原生时代的蜕变
  2. Spring单例的线程安全性
  3. 三年深入探索,网易云信让在线医疗做到技术“在线”
  4. 分布式ID生成器(来源:架构师之路,2017-06-25 58沈剑 架构师之路)
  5. 基于mysql数据库binlog的增量订阅消费
  6. CentOS 修改线程数限制等(limits.conf)
  7. sap.dfa.help.utils.adapters.hm.myadapter
  8. laravel CURD
  9. Lync Server 2010迁移至Lync Server 2013部署系列 Part13:DNS记录变更
  10. 2-OAuth2腾讯微博开放平台API小试
  11. 华为交换机Console密码重置、设备初始化、默认密码
  12. android 友盟统计功能,Android应用中添加友盟统计
  13. Linux 常用命令参考手册, 非常适合入门, 基本能满足工作日常使用。
  14. html+css 制作小米商城主体内容的商品展示
  15. UIAccelerometer 加速计(iOS5之前)
  16. 详解 .Net6 Minimal API 的使用方式
  17. 新浪cn邮箱设置收件和发件服务器信息,新浪cn邮箱如何设置
  18. 微信小程序答题,怎么设计页面渲染,答完一题,跳到下一题
  19. 百度快照排名推广如何优化?
  20. 3.1.13.fcntl函数介绍

热门文章

  1. 仅用软件FREIA_jimyu,三步刷机法(不更新EEP)
  2. NeHe的OpenGL教程笔记
  3. 优化问题-LP,QP和QCQP(线性规划,Linear Programming; 二次规划,Quadratic Programming;二次约束二次规划)
  4. python逐行运行_python逐行执行
  5. java.net.SocketTimeoutException: Read timed out异常解决方法
  6. 【转】XP开机启动一直停留在滚动条界面的解决方案
  7. 亚洲零售与制造业深刻变化,AI与SaaS平台重塑供应链战略
  8. REST API Design
  9. Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字
  10. 视频加入马赛克_会声会影篇