数组
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
数组
1数组的概念
同一种类型数据的集合。其实数组就是一个容器。运算的时候有很多数据参与运算,那么首先需要做的是什么.不是如何运算而是如何保存这些数据以便于后期的运算,那么数组就是一种用于存储数据的方式,能存数据的地方我们称之为容器,容器里装的东西就是数组的元素, 数组可以装任意类型的数据,虽然可以装任意类型的数据,但是定义好的数组只能装一种元素, 也就是数组一旦定义,那么里边存储的数据类型也就确定了。
2 数组的好处
存数据和不存数据有什么区别吗?数组的最大好处就是能都给存储进来的元素自动进行编号. 注意编号是从0开始。方便操作这些数据。
3数组的格式
元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
示例:int[] arr = new int[5];
1声明数组变量
为了使用数组必须在程序中声明数组,并指定数组的元素类型
=左半部分:
先写左边明确了元素类型 是int ,容器使用数组,那么如何来标识数组?.那么用一个特殊的符号[]中括号来表示。想要使用数组是需要给数组起一个名字的,那么我们在这里给这个数组起名字为x .接着跟上等号。
代码体现:
int [] x
注意:int x[] 也是一种创建数组的格式。推荐使用int [] x 的形式声明数组。
2创建数组
=右半部分:
要使用一个新的关键字.叫做new。new 用来在内存中产生一个容器实体,数据要存储是需要有空间的,存储很多数据的空间用new 操作符来开辟,new int[3]; 这个3是元素的个数。右边这部分就是在内存中定义了一个真实存在的数组,能存储3个元素。
new int[3] 做了两件事情,首先使用new int[3] 创建了一个数组,然后把这个数组的引用赋值给数组变量x。
int [] x=new int[3];
x 是什么类型?
任何一个变量都得有自己的数据类型。注意这个x 不是int 类型的 。int 代表的是容器里边元素的类型。那么x 是数组类型的。
数组是一种单独的数据类型。数据类型分为2大派,分为基本数据类型和引用数据类型。 第二大派是引用数据类型。那么大家现在已经接触到了引用数据类型三种当中的一种。就是数组类型 [] 中括号就代表数组。
4、int[] arr = new int[5];在内存中发生了什么?
运行任何一个程序,运行的时候都需要在内存中开辟空间.int[] arr = new int[5]; 这个程序在内存中是什么样?这就涉及到了java虚拟机在执行程序时所开辟的空间,那么java开辟启动了多少空间呢?继续学习java的内存结构。
1.1.1. 数组的定义
格式1:
元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
示例:int[] arr = new int[5];
int arr[] =new int[5];
格式2:
元素类型[] 数组名 = new 元素类型[]{元素,元素,……};
int[] arr = new int[]{3,5,1,7};
int[] arr = {3,5,1,7};
注意:给数组分配空间时,必须指定数组能够存储的元素个数来确定数组大小。创建数组之后不能修改数组的大小。可以使用length 属性获取数组的大小。
遍历数组
1.1.2. 数组初始化
数组的格式
int[] x = new int[3];
x[0] = 1;
x[1] = 2;
另一种定义:该形式可以直接明确数组的长度,以及数组中元素的内容
int[] x = { 1, 2, 3 };
int[] x=new int[]{1,2,3};
初始化方式1:不使用运算符new
int[] arr = { 1, 2, 3, 4, 5 };
int[] arr2 = new int[] { 1, 2, 3, 4, 5 };
初始化方式2:
int[] arr3=new int[3];
arr3[0]=1;
arr3[1]=5;
arr3[2]=6;
如果数组初始化中不使用运算符new。需要注意:下列写法是错误的。
int[] arr;
arr={1,2,3,4,5};
此时初始化数组,必须将声明,创建,初始化都放在一条语句中个,分开会产生语法错误。
所以只能如下写:
int[] arr={1,2,3,4,5};
1.1.3. 数组遍历
数组中有一个属性可以获取到数组中元素的个数,也就是数组的长度. 数组名.length
public static void main(String[] args) {
int[] x = { 1, 2, 3 };
for (int i = 0; i < x.length; i++) {
System.out.println(x[y]);
}
}
1.1.4. 数组的常见异常
一数组角标越界异常:注意:数组的角标从0开始。
public static void main(String[] args) {
int[] x = { 1, 2, 3 };
System.out.println(x[3]);
//java.lang.ArrayIndexOutOfBoundsException
}
二 空指针异常:
public static void main(String[] args) {
int[] x = { 1, 2, 3 };
x = null;
System.out.println(x[1]);
// java.lang.NullPointerException
}
数组:
什么时候使用数组:当元素较多时为了方便操作这些数组,会先进行来临时存储,所使用的容器就是数组。
特点:
数组长度是固定的。
1.1.5. 数组的常见操作
1:定义一个遍历数组的函数
2:定义一个求数组和的功能函数
3:定义一个获取数组最大值的功能函数
4:定义一个获取数组最大值角标的功能函数
5:定义一个返回指定数在指定数组中包含的角标的功能函数
6:定义一个可以用于排序int数组的函数
可以操作 {10,8,7,2,5};
1:冒泡
2:选择
7:定义一个可以将整数数组进行反序的功能函数。
8:二分查找(必须是有序的 )
基本操作:
1、获取最值(最大值,最小值)
2、排序(选择排序,冒泡排序)
3、折半查找(二分查找)
1.1.5.1. 获取最值
1获取最大值,
2获取最小值,
3使用0进行比较的问题(元素全是负数)。定义第三方变量的问题。
public static int getMax(int[] arr) {
int max = 0;
for (int x = 0; x < arr.length; x++) {
if (max < arr[x]) {
max = arr[x];
}
}
return max;
}
public static void main(String[] args) {
int[] x = new int[] { 2, 5, 6, 20, 100 };
int max=getMax(x);
System.out.println(max);
}
注意:如果数组中有负数(元素全是负数),还可以吗?
所以:初始化为0,正整数没问题如果是负数。那么就初始化为元素的任意一个元素
public static int getMax(int[] arr) {
int max = arr[0];
for (int x = 0; x < arr.length; x++) {
if (max < arr[x]) {
max = arr[x];
}
}
return max;
}
注意:定义变量时候初始化为0, 其实也是可以的。让变量作为角标就行了。
public static int getMax(int[] arr) {
int max = 0;
for (int x = 0; x < arr.length; x++) {
if (arr[max] < arr[x]) {
max = x;
}
}
return arr[max];
}
1.1.5.2. 数组的排序
想要对一个数组排序例如int[] arr={4,5,2,1,7},按照从小到大的顺序(升序)排列。
1、冒泡排序(bubble sort)
冒泡排序,每次两个相邻的值进行比较,内层循环结束,最后出现最大值
2、选择排序(selection sort)
选择排序先找到数组中最小的数,然后将它放在数组的最前边,然后在剩下的数组找到最小数,将它放在第一个数的后边,以此类推,知道该数组仅剩一个数为止。
/*
冒泡排序,每次两个相邻的值进行比较,内层循环结束,最后出现最大值。
内层循环 arr.length-1 避免角标越界
arr.length-x 减少参与比较的数,因为这些数,已经是最大值,排在最后,没有必要参与比较。
*/
public static void bubbleSort(int[] arr){
for(int x=0;x<arr.length;x++){
for(int y=0;y<arr.length-1-x;y++){
if(arr[y]>arr[y+1]){
int temp=0;
temp=arr[y+1];
arr[y+1]=arr[y];
arr[y]=temp;
}
}
}
}
/*
选择排序。内循环结束 0角标位出现最小值。只要外层循环的值比内层循环值大,就互换位置。
0角标位的元素,和0角标位以后的每个元素进行比较,只要比他们大就互换,位置,这样可以保证0角标位置值最小。
然后,再进行1角标位置和1角标后的每个元素进行比较。
依次类推,
*/
public static void selectSort(int[] arr){
for(int x=0;x<arr.length-1;x++){
for(int y=x+1;y<arr.length;y++){
if(arr[x]>arr[y]){
int temp=arr[y];
arr[y]=arr[x];
arr[x]=temp;
}
}
}
}
1.1.5.3. 数组折半查找(二分查找)
普通的查找适用于小数组或者没有排序的数组中,但是如果数组很大,效率会很低。二分查找效率较高,但是数组要排序。
数组折半查找必须对于有序数组。
本例中使用升序排列。二分查找首先将关键字与数组的中间元素进行比较。
如果关键字小于中间元素,只需要在数组的前一半元素中据需查找关键字。
如果关键字大于中间元素,只需要在数组的后一半元素中继续查找关键字。
如果关键字等于中间元素,则匹配成功查找结束。
所以二分查找的每次比较之后就会排除掉一半的数组元素
定义变量min 和max分别表示数组的头角标和尾角标第一次比较是min是0,max是数组.length-1。并且定义变量mid记录数组的中间元素的角标。mid=(min+max)/2;
重复的比较需要使用循环,使用while即可,控制循环的条件是min<=max 即可。
注意:为什么要用等号(=)如果数组中只有一个元素,这个查找就会漏掉这个元素。
练习:
对数组中的元素进行查找,获取该元素第一次在数组中出现的位置。
public static int getIndex(int[] arr, int key) {
for (int x = 0; x < arr.length; x++) {
if (arr[x] == key) {
return x;
}
}
return -1;
}
折半查找
通过定义变量,记录着头角标min,尾角标max,和中间角标mid,
初始化的变量为min = 0; max = arr.length - 1; mid = (max + min) / 2;
只要头角标小于等于尾角标,意味着还有数字可查。就可以一直进行循环判断和查找。
根据想要查找的key 进行判断,如果该数大于arr[midd] 那么意味着先要查找的数在midd角标的右边,需要重新计算头角标,从midd+1 开始。尾角标不变。
如果 key<arr[midd] 意味着想要查找的数在midd的左边,需要重新计算尾角标。
这样循环。通过if else if 语句,如果if else if 都不满足,就是既不大于,又不小于,那么就是等于。说明查找的数正对应midd。直接return 就可以了。
要考虑没有查找到。通过返回-1 来表示没有找到查找的数。
public static int halfSearch(int[] arr, int key) {
int max, min, mid;
min = 0;
max = arr.length - 1;
while (min <= max) {
mid = (max + min) / 2;
if (key > arr[mid])
min = mid + 1;
else if (key < arr[mid])
max = mid - 1;
else
return mid;
}
return -1;
}
1.1. 内存结构
Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,有对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
栈内存
l 用于存储局部变量,当数据使用完,所占空间会自动释放。
堆内存
l 数组和对象,通过new建立的实例都存放在堆内存中。
l 每一个实体都有内存地址值
l 实体中的变量都有默认初始化值
l 实体不在被使用,会在不确定的时间内被垃圾回收器回收
方法区,本地方法区,寄存器
java启动时一共在内存中开辟了5块空间,来进行自己数据的存储如方法区,本地方法区,寄存器,同时还有两个区域栈内存和堆内存。现在我们了解一下栈内存和堆内存。
栈内存:
public static void main(String[] args) {
int x = 0;
show();
}
public static void show() {
int x = 10;
System.out.println(x);
}
加载类,main方法进栈,初始化x ,show方法进栈,show出栈,main方法执行完毕.
当在主函数中定义了int x=3主函数运行的时候会在栈内存中开辟一块空间 就是 x=3; 这个区域就属于主函数区域。那么这个变量是在栈内存中的。
那么如果在main函数中调用show函数,show 函数也会在栈内存中开辟空间, show 函数中定义了变量x=3.那么会不会重复呢?是不会重复的 。当show函数一被执行完,,就在栈内存中消失了,所以show中的局部变量也跟着消失了。
局部变量, 定义在方法中的变量,定义在方法参数上变量,定义在for循环的变量全都是局部的。局部变量都是在栈内存中的。
内存为什么划分这么多空间?为什么把内存空间划分那么的细。因为每一片内存空间处理方式不一样。对于栈内存中数据只要用完就会自动释放。但是堆内存不是,这就是数据处理方式不一样。
堆内存:
int [] x=new int[3]; 就和栈内存有很大的不同了.这行代码在内存中是怎样的?
首先 int [] x 在栈内存中开辟了空间 了,记住凡是局部变量都是在栈内存中的,那么局部变量会在栈内存中开辟空间 定义了一个x 。
new 出来的实体都在堆内存里。实体包括数组和对象。
new 就在堆内存中开辟了空间,那么堆中的数据和栈内存中的数据是怎么联系起来的呢.
在栈中的变量 x如何和堆中的内存空间建立联系?
1 堆内存的首地址值(引用).
堆内存的特点.堆内存的每一个实体都有自己的存放位置.内存中都是地址值.用地址来标识数据存放的位置.那么数组在内存中存放的时候总有一个起始位置,这个起始位置用16进制表示(二进制表示太长) 0x0066 那么这个地址就作为数组在堆内存中存放的首地址值.那么用0x0066 标识数组在堆内存的位置的话,只需要0x0066 赋值给x 注意赋值并不是数组,而是数组的首地址.这样一赋完值x就有值了,就叫做x 指向了 这个数组.或者叫x 引用了这个数组..这就是引用数据类型,数组并没有存到x 变量中去,只是把首地址值赋给了x 变量.
这就是堆内存的第一个特点,堆内存中的每一个数组都有一个首地址值.就像教室一样,教室上边有门牌号码.。我告诉你门牌号,就可以找到这个房间。
2 堆内存中的变量都有默认初始化值。
public static void main(String[] args) {
int[] x = new int[3];
System.out.println(x[0]); // 0
System.out.println(x[1]); // 0
System.out.println(x[2]); // 0
}
并没有往数组中存入数据,只是确定了数组的存放元素的个数。那么仍旧有默认的初始化值。
堆内存的第二个特性.堆内存的实体是用于封装数据的.而堆内存中的数据都有默认初始化值.在不赋值的情况下都有默认值。
int 型默认是0 double是0.0 float是0.0f 如果是 boolean类型的默认是false。
public static void main(String[] args) {
int[] x = new int[3];
x=null;
}
如果不需要让x 指向具体的值,怎么办?
可以使用常量中的null ,只有引用数据类型才可以使用null
x=null ;是指 x不在指向具体的值了,不在指向数组的首地址值。那么跟数组就没有关系了。没关系后数组在堆内存中就没人使用了。当一个实体在堆内存中没有任何引用指向它的时候,我们就视它为垃圾。垃圾不会立刻被jvm 在内存中清除掉。而是不定时的时间内jvm 启动一个垃圾回收机制。将数组实体在堆内存中清除。
总结:
栈内存和堆内存是有很大的不同.
栈内存中的数据是自动释放,堆内存是垃圾回收.
练习:
public static void main(String[] args) {
int[] x = new int[3];
int[] y = x;
y[1] = 80;
x = null;
}
第一行 声明并创建了一个数组x x持有数组在堆内存中的引用.
第二行声明了数组y, 并把x 持有的引用赋值给了y. 所以y和x 也指向同一个数组
第三行 y[1]=80 ;数组中1角标位的值变为了80 。
注意了:两个引用指向了同一个数组。y 把数组的指定角标位的值改变了,那么 x 回来访问该角标的值的时候,是改变后的值。
第四 给x重新赋值为null. x=null 的时候x 不再指向数组,y 仍然指向数组。仍然有引用指向数组.
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------