数组
数组的概念
数组的定义
数组是相同类型数据的有序集合。其中, 每一个数据称作一个元素, 每个元素可以通过一个 索引 (下标) 来访问它们。
数组的四个基本特点:
- 长度是确定的。数组一旦被创建, 它的大小就是不可以改变的。
- 其元素的类型必须是相同类型, 不允许出现混合类型。
- 数组类型可以是任何数据类型, 包括基本类型和引用类型。
- 数组变量属于引用类型, 数组也是对象,数组中的元素相当于对象的属性!
创建数组和初始化
数组的声明方式(以一维数组为例)
type[ ] arr_name; //方式一
type arr_name[ ]; //方式二
注意事项
口 声明的时候并没有实例化任何对象, 只有在实例化数组对象时, JVM 才分配空间, 这时 才与长度有关。
口 声明一个数组的时候并没有数组真正被创建。
口 构造一个数组, 必须指定长度。
【示例】创建基本类型一维数组
public class Test {
public static void main(String args[]){
int[] s;//声明数组;
s=new int[10];//给数组分配空间;
for(int i=0;i<10;i++){
s[i]=2*i+1;//给数组赋值;数组是对象,数组中的元素就是对象的属性
System.out println(s[i]);
}
}
}
【示例】创建引用类型一维数组
class Man{
private int age;
private int id;
public Man(int id,int age){
super();
this.age=age;
this.id=id;
}
}
public class AppMain{
public static void main(String[] args){
Man[] mans;//声明引用类型数组;
mans=new Man[10];//给引用类型数组分配空间;
Man m1=new Man(1,11);
Man m2=new Man(2,22);
mans[0] =m1;//给引用类型数组元素赋值;
mans[1] =m2;//给引用类型数组元素赋值;
}
}
初始化
数组的初始化方式总共有三种: 静态初始化、动态初始化、默认初始化。
- 静态初始化
除了用 new 关键字来产生数组以外, 还可以直接在定义数组的同时就为数组元素分配 空间并赋值。
【示例】数组的静态初始化
int[] a={1,2,3};//静态初始化基本类型数组;
Man[] mans={ new Man(1,1),new Man(2,2)};//静态初始化引用类型数组;
- 动态初始化
数组定义与为数组元素分配空间并赋值的操作分开进行。
【示例】数组的动态初始化
int[] a1=new int[2];//动态初始化数组,先分配空间;
a1[0]=1;//给数组元素赋值;
a2[1]=2;//给数组元素赋值;
- 默认初始化
数组是对象, 它的元素相当于对象的属性; 每个元素也按照属性的方式被默认初始化。
【示例】数组的默认初始化
int a2 [ ] = new int[2]; // 默认值: 0,0
boolean[ ] b = new boolean[2]; // 默认值: false,false
String[ ] s = new String[2]; // 默认值: null, null
数组常见操作
- 遍历指的就是“通过循环遍历数组的所有元素”。
- 拷贝指的是将某个数组的内容拷贝到另一个数组中
注:实质上, 后面大家学容器的扩容就是“数组的拷贝”。
数组的遍历
数组元素下标的合法区间: [0, length-1]。我们可以通过下标来遍历数组中的元素, 遍 历时可以读取元素的值或者修改元素的值。
【示例】使用循环初始化和遍历数组
public class Test {
public static void main(String[ ] args) {
int[] a=new int[4];
//初始化数组元素的值
for(int i=0;i<a.length;i++){
a[i]=100*i;
}
//读取元素的值
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
}
运行结果:
for-each 循环
for-each 专门用于读取数组或容器中所有的元素
【示例】使用增强 for 循环遍历数组
public class Test {
public static void main(String[ ] args) {
String[] ss= { "aa", "bbb", "ccc", "ddd" };
for (String temp : ss) {
System.out.println(temp);
}
}
}
运行结果:
注意事项
口 for-each 增强 for 循环在遍历数组过程中不能修改数组中某元素的值。
口 for-each 仅适用于遍历, 不涉及有关索引 (下标) 的操作。
数组的拷贝
System.arraycopy(object src,int srcpos,object dest, int destpos,int length)
该方法可以将 src 数组里的元素值赋给 dest 数组的元素,其中 srcpos 指定从 src 数组的第几个 元素 开始赋值,length 参数指定将 src 数组的多少个元素赋给 dest 数组的元素。
【示例】数组的拷贝
public class Test {
public static void main(String args[ ]) {
String[ ] s = {"阿里","尚学堂","京东","搜狐","网易"};
String[ ] sBak = new String[6];
System.arraycopy(s,0,sBak,0,s.length);
for (int i = 0; i < sBak.length; i++) {
System.out.print(sBak[i]+ "\t");
}
}
}
运行结果:
java.util.Arrays 类
Arrays 类包含了: 排序、查找、填充、打印内容等常见的数组操作
【示例】使用 Arrays 类输出数组中的元素
import java.util.Arrays;
public class Test {
public static void main(String args[ ]) { int[ ] a = { 1, 2 };
System.out.println(a); // 打印数组引用的值;
System.out.println(Arrays.toString(a)); // 打印数组元素的值;
}
}
运行结果:
注意
此处的 Arrays.toString()方法是 Arrays 类的静态方法,不是前面讲的 Object 的 toString() 方法。
【示例】使用 Arrays 类对数组元素进行排序一
import java.util.Arrays;
public class Test {
public static void main(String args[ ]) {
int[ ] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println(Arrays.toString(a));
}
}
运行结果:
【示例】使用 Arrays 类实现二分法查找法
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
int[ ] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.sort(a); //使用二分法查找, 必须先对数组进行排序;
System.out.println(Arrays.toString(a));
//返回排序后新的索引位置,若未找到返回负数。
System.out.println("该元素的索引: " +Arrays.binarySearch(a, 12));
}
}
【示例】使用 Arrays 类对数组进行填充
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
int[ ] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.fill(a, 2, 4, 100); //将2到4索引的元素替换为100;
System.out.println(Arrays.toString(a));
}
}
运行结果:
多维数组
多维数组可以看成以数组为元素的数组。可以有二维、三维、甚至更多维数组, 但是实际开发中用 的非常少。最多到二维数组(学习容器后, 我们一般使用容器, 二维数组用的都很少) 。
【示例】二维数组的声明
public class Test {
public static void main(String[ ] args) {
// Java中多维数组的声明和初始化应按从低维到高维的顺序进行
int[ ][ ] a = new int[3][ ];
a[0] = new int[2];
a[1] = new int[4];
a[2] = new int[3];
// int a1[ ][ ]=new int[ ][4];//非法
}
}
【示例】二维数组的静态初始化
public class Test {
public static void main(String[ ] args) {
int[ ][ ] a = { { 1, 2, 3 }, { 3, 4 }, { 3, 5, 6, 7 } };
System.out.println(a[2][3]);
}
}
【示例】二维数组的动态初始化
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
int[ ][ ] a = new int[3][ ];
// a[0] = {1,2,5}; //错误, 没有声明类型就初始化
a[0] = new int[ ] { 1, 2 };
a[1] = new int[ ] { 2, 2 };
a[2] = new int[ ] { 2, 2, 3, 4 };
System.out.println(a[2][3]);
System.out.println(Arrays.toString(a[0]));
System.out.println(Arrays.toString(a[1]));
System.out.println(Arrays.toString(a[2]));
}
}
运行结果:
数组存储表格数据
表格是计算机世界最普遍的模型。大家在互联网上看到的所有数据本质上都是“表格”。
每一行可以使用一个一维数组存储:
Object[ ] a1 = {1001, “吴嗲”,18, “讲师”,“2-14”};
Object[ ] a2 = {1002, “吴小雅”,19, “助教”,“10-10”};
Object[ ] a3 = {1003, “吴小小雅”,20, “班主任”,“5-5”};
注意事项
口 此处基本数据类型”1001”, 本质不是 Object 对象。JAVA 编译器会自动把基本数据 类型“自动装箱”成包装类对象。大家在下一章学了包装类后就懂了。
只需要再定义一个二维数组, 将上面 3 个数组放入即可:
Object[ ][ ] emps = new Object[3][ ];
emps[0] = a1;
emps[1] = a2;
emps[2] = a3;
【示例】使用二维数组保存表格数据
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
Object[ ] a1 = {1001, "吴嗲",18, "讲师","2-14"};
Object[ ] a2 = {1002, "吴小雅",19, "助教","10-10"};
Object[ ] a3 = {1003, "吴小小雅",20, "班主任","5-5"};
Object[ ][ ] emps = new Object[3][ ];
emps[0] = a1;
emps[1] = a2;
emps[2] = a3;
System.out.println(Arrays.toString(emps[0]));
System.out.println(Arrays.toString(emps[1]));
System.out.println(Arrays.toString(emps[2]));
}
}
【示例】使用 javabean 和一维数组保存表格信息
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
Emp[] emps = {
new Emp(1001,"吴嗲",18,"讲师","2-14"),
new Emp(1002,"吴小雅",19,"助教","10-10"),
new Emp(1003,"吴小小雅",20,"班主任","5-5")
};
for (Emp e:emps){
System.out.println(e);
}
}
}
class Emp {
private int id;
private String name;
private int age;
private String job;
private String hiredate;
public Emp(int id, String name, int age, String job, String hiredate) {
this.id = id;
this.name = name;
this.age = age;
this.job = job;
this.hiredate = hiredate;
}
@Override
public String toString() {
return "[" +id+"," +name+"," +age+"," +job+"," +hiredate+"]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getHiredate() {
return hiredate;
}
public void setHiredate(String hiredate) {
this.hiredate = hiredate;
}
}
Comparable 接口
多个对象做比较, 就要有“比较规则”, 后实现排序。事实上,java中排序算法的底层也依赖 Comparable接口。
Comparable 接口中只有一个方法: public int compareTo(Object obj) 【obj
为要比较的对象】
方法中,将当前对象和obj这个对象进行比较,如果大于返回 1,等于返回0,小于返回-1. (此 处的 1也可以是正整数,
-1也可以是负整数)。 compareTo方法的代码也比较固定:
public int compareTo(Object o) {
Man man = (Man) o;
if (this.age < man.age) {
return -1;
}
if (this.age > man.age) {
return 1;
}
return 0;
}
【测试 Comparable 接口】使用 Arrays 类对数组元素进行排序二
import java.util.Arrays;
public class test {
public static void main(String[] args) {
Man[ ] msMans = { new Man(3, "a"), new Man(60, "b"), new Man(2, "c") }; Arrays.sort(msMans);
System.out.println(Arrays.toString(msMans));
}
}
class Man implements Comparable {
int age;
int id;
String name;
public Man(int age, String name) {
super();
this.age = age;
this.name = name;
}
public String toString() {
return this.name;
}
public int compareTo(Object o) {
Man man = (Man) o;
if (this.age < man.age) {
return -1;
}
if (this.age > man.age) {
return 1;
}
return 0;
}
}
常见算法
冒泡排序算法
冒泡排序的基础算法
冒泡排序算法重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就 把他们交换过来, 这样越大的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运作如下:
- 比较相邻的元素。如果第一个比第二个大, 就交换他们两个。
- 对每一对相邻元素作同样的工作, 从开始第一对到结尾的最后一对。在这一点, 最 后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤, 除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤, 直到没有任何一对数字需要比较。
【示例】 冒泡排序的基础算法
import java.util.Arrays;
public class TestBubbleSort {
public static void main(String[] args) {
int[] values = {3, 1, 6, 8, 9, 0, 7, 4, 5, 2};
bubleSort(values);
System.out.println(Arrays.toString(values));
}
public static void bubbleSort(int[] values) {
int temp;
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values.length - 1 - i; j++) {
if (values[j] > values[j + 1]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
}
}
}
}
}
冒泡排序的优化算法
我们可以把上面的冒泡排序的算法优化一下, 基于冒泡排序的以下特点:
1.整个数列分成两部分: 前面是无序数列, 后面是有序数列。
2.判断每一趟是否发生了数组元素的交换, 如果没有发生, 则说明此时数组已经有序, 无需再进行后续趟数的比较了。此时可以中止比较。
【示例】 冒泡排序的优化算法
import java.util.Arrays;
public class TestBubbleSort2 {
public static void main(String[ ] args) {
int[ ] values = { 3, 1, 6, 8, 9, 0, 7, 4, 5, 2 };
bubleSort(values);
System.out.println(Arrays.toString(values));
}
public static void bubbleSort2(int[ ] values) {
int temp;
for (int i = 0; i < values.length ; i++) {
// 定义一个布尔类型的变量, 标记数组是否已达到有序状态
boolean flag = true;
/*内层循环: 每一趟循环都从数列的前两个元素开始进行比较, 比较到无序
数组的最后*/
for (int j = 0; j < values.length - 1 - i; j++) {
// 如果前一个元素大于后一个元素, 则交换两元素的值;
if (values[j] > values[j + 1]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
//本趟发生了交换,表明该数组在本趟处于无序状态,需要继续比较;
flag = false;
}
}
//根据标记量的值判断数组是否有序,如果有序,则退出;无序,则继续循环。
if (flag) {
break;
}
}
}
}
运行结果:
二分法查找
二分法检索 (binary search) 又称折半检索。
eg:数组[7, 8, 9, 10, 12, 20, 30, 40, 50, 80, 100]中查询 10 是否存在?
二分法检索的基本思想
-
是设数组中的元素从小到大有序地存放在数组 (array) 中, 首先将给定值 key。
-
与数组中间位置上元素的关键码(key)比较, 如果相等则检索成功; 否则若 key 小, 则在数组前半部分中继续进行二分法检索; 若 key 大,则在数组后半部分中继续进行二分法检索。
-
这样,经过一次比较就缩小一半的检索区间,如此进行下去,直到检索成功或检索失败。 二分法检索是一种效率较高的检索方法。
【示例】二分法查找法的基本算法
import java.util.Arrays;
public class TestBinarySearch {
public static void main(String[ ] args) {
int[ ] arr = { 30,20,50,10,80,9,7,12,100,40,8};
int searchWord = 20; // 所要查找的数
Arrays.sort(arr); //二分法查找之前, 一定要对数组元素排序
System.out.println(Arrays.toString(arr));
System.out.println(searchWord+"元素的索引: " +binarySearch(arr,searchWord));
}
public static int binarySearch(int[ ] array, int value){
int low = 0;
int high = array.length - 1;
while(low <= high){
int middle = (low + high) / 2;
if(value == array[middle]){
return middle; //返回查询到的索引位置
}
if(value > array[middle]){
low = middle + 1;
}
if(value < array[middle]){
high = middle - 1;
}
}
return -1; //上面循环完毕, 说明未找到, 返回-1
}
}
运行结果: