引言
现在目前我们的一个变量,最多就保存一个数据,显然不科学。目前需要一个方案来解决这个问题,可以保存多个数据的容器。在几乎所有的编程语言中都提供了数组这么一个概念,数组它是一种基础的数据结构(列表数据结构)。
一. 数组概念
1.1 基本概念
数组它是存放 一组类型相同,个数有限的数据的容器。数组也是变量,由于它的数据结构的特点可以保存多个数据,数组在内存中,往往是块连续有限的空间。
1.2 图形理解
二 . 数组的声明与赋值
数组它是一种引用数据类型,它的存储需要使用到两块内存空间(堆和栈),基本数据类型只需要栈空间
2.1 数组声明语法
声明:
声明并分配空间 int[] a = new int[5];
先声明 再分配空间 int a =[]; a =new int[5]; 局部变量存在栈中
声明并赋值(繁) int [] b; b=new int[]{11,22,33,44,55};
声明并赋值(简) int [] c = {1,2,3,4,5};
2.2 数组内存分配
数组初始化后,会为元素赋予初始值,比如整型数组默认为0 浮点数数组默认为0.0 引用数据类型数组为 null。
三 . 数组的组成
3.1 数组元素寻址
数组由各个元素构成,各个元素依次排列,每个元素都有一个寻址下标,选址下标自动生成,从0开始。首地址结合下标通过偏移就可以寻址到每个元素,这里有一个问题就是,如果偏移的过程中,超过了下标,会造成数组下标越界问题。最大下标为数组长度-1。
3.2 获得数组长度
3.3 数组下标越界
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 这个异常就是数组下标超出了范围。
下标的范围:0 - 数组长度减一
数组默认值:(没有赋值的情况下)
整数:0
小数:0.0
字符:\u0000
布尔:false
其他:null
public static void main(String[] args) {
int[] scores = new int[5];
//存数据:赋值
scores[0] = 1;
scores[1] = 2;
scores[2] = 3;
scores[3] = 4;
scores[4] = 5;
//取数据:
System.out.println( scores[0] );
System.out.println( scores[1] );
System.out.println( scores[2] );
System.out.println( scores[3] );
System.out.println( scores[4] );
}
四 . 数组的遍历
数组遍历,指的是将元素依次访问一遍,通常这个过程需要借助循环实现,这里介绍两种常见遍历数组的方式
4.1 通过普通for循环
借助循环的连续的循环变量作为下标。
public static void main(String[] args) {
int[] scores = new int[5];
//存数据:赋值
scores[0] = 1;
scores[1] = 2;
scores[2] = 3;
scores[3] = 4;
scores[4] = 5;
//取数据:
System.out.println( scores[0] );
System.out.println( scores[1] );
System.out.println( scores[2] );
System.out.println( scores[3] );
System.out.println( scores[4] );
//数组遍历(把每个元素都访问一遍 != 输出 )
System.out.println("---遍历方式1--"); //天意:循环计数器可以是连续的,恰好用在这里当下标
for( int i =0 ; i< scores.length ; i++ ){
System.out.println( scores[i] );
}
Arrays.sort()
Scnnar sc = new Scanner()
sc.nextInt();
}
4.2 通过增强for循环foreach
此方法是一种语法糖,也就是编写上的形式方便,底层依然采用普通for实现
public static void main(String[] args) {
int[] scores = new int[5];
//存数据:赋值
scores[0] = 1;
scores[1] = 2;
scores[2] = 3;
scores[3] = 4;
scores[4] = 5;
//取数据:
System.out.println( scores[0] );
System.out.println( scores[1] );
System.out.println( scores[2] );
System.out.println( scores[3] );
System.out.println( scores[4] );
//数组遍历 (增强for循环 语法糖= 编译的时候会过程叫 解语法糖,会采用基础原生的方式实现 )
System.out.println("---遍历方式2--");
for( int element : scores ){
System.out.println(element);
}
}
五. 数组应用
5.1 基本使用
5.2 数组作为参数
5.3 数组作为返回值
/**
数组作为返回值类型
*/
public class ArrayUesdReturn {
//编写一个程序,生成一个数字验证码 (4位)
public static int[] createCode(){
int[] codeArray = new int[4];
Random rd = new Random();
for(int i=0;i<4;i++){
int n = rd.nextInt(10);
codeArray[i]=n;
}
System.out.println("codeArray的地址"+codeArray);
return codeArray;
}
public static void main(String[] args) {
int[] result = createCode(); // ctrl
System.out.println("result的地址"+result);
ArrayUsedArgs.printArray(result);
}
}
5.4 值传递与地址传递
- 对于基本数据类型,传参为值的拷贝,当然返回的也是值的拷贝。
- 对于引用数据类型, 传参实际是地址传递,当然返回的也是地址。 ```java public class ValueAndRefrence {
public static void main(String[] args) {
int age = 10;
change(age);
System.out.println(age); // 10
int[] brr = {10,20};
change2(brr);
System.out.println(brr[0]+" "+brr[1]);// 100 20
}
public static void change(int num ){
num = 100;
}
public static void change2(int[] arr){
arr[0] = 100;
}
}
<a name="WEN3D"></a>
#### 5.5 数组的扩容
什么是扩容?<br />扩容指的是扩展存储空间容量,
为什么要扩容?<br />数组是的容量是声明是就确定了的(定死了)后续不可以被修改,但是我们实际情况又需要更多的容量。这里的"扩容"是一种通过其他手段实现的。因为数组本身长度声明后是不可改变的。
```java
旧:
第一种:
int [] c = {1,2,3,4,5};
int [] new_c = new int [10];
for(int i=0;i<c.length;i++)
{
new_c[i]=c[i];
}
for (int i = 0; i <new_c.length ; i++)
{
System.out.println(new_c[i]);
}
第二种:
System.arraycopy(原数组,原数组起始,新数组,新数组起始,取到元素的个数);
System.arraycopy(c,0,new_c,0,5);
第三种:
java.util.Arrays.copyOf(原数组,新长度);
int[] new_c = Arrays.copyOf(c,10);
c=new_c;
//第一种扩容
System.out.println("第一种扩容");
int [] c = {1,2,3,4,5};
int [] new_c = new int [10];
for(int i=0;i<c.length;i++){
new_c[i]=c[i];
}
for (int i = 0; i <new_c.length ; i++)
{
System.out.println(new_c[i]);
}
//第二种扩容
System.out.println("第二种扩容");
int[] new_c2=new int[5];
System.arraycopy(c,1,new_c2,0,3);
for (int i = 0; i <new_c2.length ; i++)
{
System.out.println(new_c2[i]);
}
//第三种扩容
System.out.println("第三种扩容");
int[] new_c3 = Arrays.copyOf(c,10);
for (int i = 0; i <new_c3.length ; i++)
{
System.out.println(new_c3[i]);
}
5.6 数组元素删除
//封装一个删除方法(可能并不完美哈)
public static void delete( String[] data , int index ){
System.arraycopy( data, index+1,data,index,data.length-1-index );
int l = data.length-1;
for(int i=data.length-1;i>0;i--){
if( data[l]==null ){
}else{
data[l]= null;
break;
}
}
}
六 . 数组排序与查找
数组排序指的是采用某种算法来将元素按一定次序排列整齐,数组的查找,根据指的元素判断其在数组中的位置
6.1 冒泡排序
取相邻的两个元素比较如果前者大于后则交换,一趟的结果为找出了这趟的最大值,且交换到最最后 ,一致重复趟数直到完成。
相邻两个值比较大小,互换位置
N个数字来排队
两两相比小考前
外层循环N-1
内层循环N-1-i
int temp;
for (int i = 0; i <a.length-1 ; i++)
{//0 1 2 3
for (int j = 0; j <a.length-i-1 ; j++)
{//0 0 1 2 3 4 1 0 1 2 3 2 0 1 2
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1] = temp;
}
}
}
for (int e:a)
{
System.out.println(e);
}
6.2 选择排序
固定一个值与其他值比较大小,互换位置
int[] a = {1,3,3,7,5,8,9};
for (int i = 0; i <a.length-1 ; i++)
{
int min =i;
for (int j = i+1; j <a.length ; j++)
{
if(a[j]<a[i]){
min= j;
}
if(min!=i){
int tmp = a[i];
a[i] = a[min];
a[min] = tmp;
}
}
}
for (int e:a)
{
System.out.print(e);
}
6.3 插入排序
public static void insert( int[] data){
//根据元素个数确定趟数 为什么是1 开始,假设第一个是排好
for( int i=1; i< data.length;i++ ){
//从未排序区取一个取一个元素data[i+1] 和 已经排序区 比较寻找和位置
for( int j=i ; j>0;j-- ){
// 1 ,2 3 4 8 3|
//如果我们选择的这个数 小于当前元素则交换
if( data[j] < data[j-1] ){
int t = data[j];
data[j]= data[j-1];
data[j-1]=t;
}
}
}
}
6.4 使用JDK排序
jdk排序:Java.util.Arrays.sort() 默认排序升序
int[] a = {1,6,3,7,5,8,9};
Arrays.sort(a);
for (int e:a) {
System.out.print(e);
}
6.5 快速排序原理
- 快速排序,特点就是块, 一般的情况 O(n*lgN ) 最坏情况是n^2
- 它是一种分治的思想。分区( 把数据 分为两个子区, 小于基准数的排列到 左边 大于基准数的排列在右边。 )
- 重复重复上述动作分区( 引入递归 )
- 确定一个基准数。(左边第一个数)
- 从左往右扫,如果小于基准数则继续前行,直到找到第一个大于基准数的值。则保存这个数到右边。
- 从右向左扫,如果大于基准数着继续前行,直到找到第一个小于基准数的值,着包装这个数到左边。
- 重复上诉过程,即可把数据分为两区。
public static void main(String[] args) {
int[] data = {38,22,44,23,14,32,28,66,25,88,77,44};
quickSort(data, 0, data.length - 1);
System.out.println(Arrays.toString(data));
}
public static void quickSort( int data[] , int left, int right ){
if( left<right ){
int index = division(data,left,right);
quickSort(data,left,index-1);
quickSort(data,index+1,right);
}
}
public static int division(int[] data , int left, int right){
int pivot = data[left];
while (left < right){
while ( left < right && data[right] >= pivot )
--right;
data[left] = data[right];
while (left<right && data[left] <= pivot )
++left;
data[right]=data[left];
}
data[left]=pivot;
return left;
}
从数组中找到目标数据,如果寻找,又涉及到相关的算法,典型查找方法,比如线性 查找,或者折半查找。
6.6 线性查找
就是从头到尾查找,如果元素非常多,效率就不高。
package find;
import java.util.Arrays;
//查找
public class FindDemo {
public static void main(String[] args) {
String[] team = { "诸葛亮","刘备","关羽","大乔","小乔","曹操" };
int index = lineFind(team,"吕布");
System.out.println(index);
//原数组
int[] data = {1,3,46,367,312,11,567,44,22,56};
//排序
Arrays.sort(data);
System.out.println(Arrays.toString(data));
int index2 = binarySerach(data, 56);
//int index2 = Arrays.binarySearch(data, 56);
System.out.println(index2);
}
// 线性查找:更通用,可适用于各种数据类型
public static int lineFind(String[] data, String key){
for(int i=0; i<data.length; i++){
if( data[i].equals(key) ){
return i;
}
}
return -1;
}
}
6.7 折半查找
就是 通过已经排好序的数组,先判断中间值,如果相等则返回,否则不断缩小查找范围
package find;
import java.util.Arrays;
//查找
public class FindDemo {
public static void main(String[] args) {
//原数组
int[] data = {1,3,46,367,312,11,567,44,22,56};
//排序
Arrays.sort(data);
System.out.println(Arrays.toString(data));
int index2 = binarySerach(data, 56);
//int index2 = Arrays.binarySearch(data, 56);
System.out.println(index2);
}
// 二分查找:针对的是数字查找,且要求数字必须先排序。
public static int binarySerach( int[] data , int key ){
int left = 0; //左起点
int right = data.length-1;//右终点
//只要 左起点下标未超过右终点 继续,否则说明 移动交叉了不存在查找的数据 停止查找。
while( left<=right ){
//计算中间值下标
int min = (left+right)/2;
// 判断与中间值的大小
if( key == data[min]){ // 查到了
return min;
}else if( key < data[min] ){ // 在左半轴查找
right = min-1;
}else{ // 在右半轴查找
left = min+1;
}
}
//没有找到
return -1;
}
}
七 . 二维数组(了解)
7.1 概念
二维数组从空间上看是有行列组成的结构,但是实际在计算机中,二维数组本质上还是一个一维数组,只是这个一维数组的每个元素,都是一个数组。
7.2 内存理解
数组的每个元素保存的右是 另一个数组的地址。
7.3 创建和声明二维数组
int[][] num = new int[3][5];
num[0][1] = 20;
num[0][2] = 30;
num[0][3] = 40;
num[1][2] = 20;
num[2][4] = 10;
for (int i = 0; i <num.length ; i++) {
for (int j = 0; j < num[i].length; j++) {
System.out.print(num[i][j]+"\t");
}
System.out.println();
}
7.4 二维数组遍历
public static void main(String[] args) {
int[][] data = new int[3][3];
//元素的访问:赋值
data[0][0] = 1;
data[0][1] = 2;
data[0][2] = 3;
data[1][0] = 4;
data[1][1] = 5;
data[1][2] = 6;
data[2][0] = 7;
data[2][1] = 8;
data[2][2] = 9;
// 普通for
for(int i=0;i<data.length;i++){
for(int j=0;j< data[i].length;j++ ){
System.out.print(data[i][j]+"\t");
}
System.out.println();
}
// 增强for
for( int[] arr : data ){
for( int e :arr ){
System.out.print(e+"\t");
}
System.out.println();
}
}
课后习题:
打印杨辉三角
static void yanghui(int a){
int[][] arr = new int[a][a];
for (int i = 0; i <a ; i++) {
arr[i][0]=1;
arr[i][i]=1;
}
for (int i = 2; i <a ; i++) {
for (int j = 1; j <i ; j++) {
arr[i][j]=arr[i-1][j-1]+arr[i-1][j];
}
}
for (int i = 0; i <arr.length ; i++) {
for (int j = 0; j <arr.length-i-1; j++) {
System.out.print(" ");
}
for (int j = 0; j <= i; j++) {
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
public class Array2 {
public static void main(String[] args)
{
int [] b; b=new int[]{11,22,33,44,55};
avg(b);
xiabiao(b);
MaxAndMin(b);
Reserve();
weizhi();
Day();
sum_OuShu();
}
/*
* 计算传入数组平均值
* */
static void avg(int[] a){
double sum=0;
double avg=0;
for (int i = 0; i < a.length; i++)
{
sum+=a[i];
}
avg=sum/a.length;
System.out.println("b数组的平均值为:"+avg);
}
/*
* 计算传入数的下标
* */
static void xiabiao(int[] b){
Scanner sc =new Scanner(System.in);
System.out.print("请输入一个数:");
int num =sc.nextInt();
for (int i = 0; i < b.length; i++)
{
if (num==b[i]) System.out.println(num+"的下标为:"+i);
}
}
/*
* 找到最大值和最小值
* */
static void MaxAndMin(int[] b){
int max = b[0];
int min = b[0];
for (int i = 0; i <b.length ; i++)
{
if(b[i]>max) max=b[i];
if(b[i]<min) min=b[i];
}
System.out.println("MAX is "+max);
System.out.println("MIN is "+min);
}
/*
* 将数组元素颠倒
* */
static void Reserve(){
int[] b; b=new int[]{11,22,33,44,55};
int[] c =new int[b.length];
for (int i = 0; i <b.length ; i++) {
c[b.length-i-1]=b[i];
}
System.out.println("原数组");
for (int i = 0; i <b.length ; i++) {
System.out.print(b[i]+"\t");
}
System.out.print("\n反转");
System.out.println();
for (int i = 0; i <c.length ; i++) {
System.out.print(c[i]+"\t");
}
System.out.println();
}
/*
* 输出积分最少的数 和 位置
* */
static void weizhi(){
int[] a = {18,25,7,36,13,2,89,63};
int min = a[0];
for (int i = 0; i <a.length ; i++)
{
if(a[i]<min) min=a[i];
}
for (int i = 0; i < a.length; i++)
{
if (min==a[i]) System.out.println("最少的积分为:"+min+"所在的下标为:"+i+1);
}
}
/*
* 输一个日期,输出这个日期是这年的第几天
* */
static void Day() {
Scanner sc = new Scanner(System.in);
System.out.print("请输入年份:");
int year =sc.nextInt();
System.out.print("请输入月份:");
int month = sc.nextInt();
System.out.print("请输入天数:");
int day = sc.nextInt();
if((year%4==0&&year%100!=0)||year%400==0){
int sum=0;
int[] arr1 = {31,29,31,30,31,30,31,31,30,31,30,31};
for (int i = 0; i < month-1; i++) {
sum+=arr1[i];
}
sum=sum+day;
System.out.println(year+"年"+month+"月"+day+"日"+"是一年中的+"+sum+"天");
}else{
int sum=0;
int[] arr2 = {31,28,31,30,31,30,31,31,30,31,30,31};
for (int i = 0; i < month-1; i++) {
sum+=arr2[i];
}
sum=sum+day;
System.out.println(year+"年"+month+"月"+day+"日"+"是一年中的"+sum+"天");
}
}
/*
* 求出下标为偶数和
* */
static void sum_OuShu(){
Scanner sc = new Scanner(System.in);
int[] arr = new int[10];
for (int i = 0; i <10 ; i++) {
System.out.print("请输入第"+(i+1)+"个数字:");
arr[i] = sc.nextInt();
}
int sum=0;
for (int i = 0; i <arr.length ; i+=2) {
sum+=arr[i];
}
System.out.println("下标为偶数的元素之和为:"+sum);
}
}