数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。例如:如果a
是一个整型数组,a[i]
就是数组中下标为i
的整数。在声明数组变量时,需要指出数组类型(数据元素类型紧跟[]
和数组变量的名字。下面声明了整型数组a:int[]a
;这条语句只声明了变量a,并没有将a初始化为一个真正的数组。应该使用new运算符创建数组。int[] a = new int[100];
这条语句创建了一个可以存储100
个整数的数组。数组长度不要求是常量: new int[n]
会创建一个长度为n
的数组。
数组本质上就是让我们能 “批量” 创建相同类型的变量。在 Java 中, 数组中包含的变量必须是相同类型。
方式一:
int[] arr = {
1,2,3,4,5};
方式二:
int[] arr2 = new int[3];//定义数组长度为3,未初始化
方式三:
int[] arr3 = new int[]{
1,2,3,4,5};//定义数组长度为5,初始化
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5};
System.out.println(arr.length);//求数组的长度
System.out.println(arr[3]);//访问数组中的元素
arr[3] = 19;
System.out.println(arr[3]);//改变数组里面的内容
}
注意:
arr.length
能够获取到数组的长度,.
这个操作为成员访问操作符,后面在面向对象中会经常用到.[ ]
按下标取数组元素. 需要注意, 下标从0
开始计数.[ ]
操作既能读取数据, 也能修改数据.[0, length - 1]
, 如果超出有效范围, 会出现下标越界异常.“遍历” 是指将数组中的所有元素都访问一遍, 不重不漏. 通常需要搭配循环语句.
for
循环可以拿到下标。
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5};
for (int i = 0; i < arr.length; i++) {
//for循环可以拿到下标
System.out.print(arr[i] + " "); //打印数组中的每个元素
}
语法格式:
for (int element : a)
System.out.println(element);
打印数组a的每一个元素,一个元素占一行。这个循环应该读作“循环a中的每一个元素”( for each element in a)。Java语言的设计者认为应该使用诸如foreach、in
这样的关键字,但这种循环语句并不是最初就包含在Java语言中的,而是后来添加进去的,并且没有人打算废除已经包含同名(例如System.in)方法或变量的旧代码。
for each
循环又称增强for
循环,可以打印数组中的每个元素 但是拿不到数组下标。
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5};
for (int x:arr) {
//
System.out.print(x + " ");
}
调用Arrays.toString(a)
,返回一个包含数组元素的字符串,这些元素被放置在括号内,并用逗号分隔,例如:“[2,3,5,7,11,13]”
。要想打印数组,可以调用:System.out.println(Arrays.toString(a));
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5};
//借助java的操作数组的工具类 Arrays.toString:将参数的数组以字符串的形式进行输出
String ret = Arrays.toString(arr);
System.out.println(ret);
}
public static void printf(int[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5};
printf(arr);//按引用传递(按值传递)
}
在这个代码中int[] a
是函数的形参, int[] arr
是函数实参.
public static void func1(int[] arr){
arr = new int[]{
10,12,24,52,61};
}
public static void func2(int[] arr){
arr[0] = 521;
}
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5};
System.out.print(Arrays.toString(arr));// 1,2,3,4,5
System.out.println();
// func1(arr);//1,2,3,4,5 虽然传的是地址,但是传过去之后,形参只是改变了自己的指向而已。
func2(arr); //521,2,3,4,5
//在函数内部修改数组内容, 函数外部也发生改变.此时数组名 arr 是一个"引用".当传参的时候, 是按照引用传参.
System.out.print(Arrays.toString(arr));
}
所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
public static void main4(String[] args) {
int[] arr = {
1,2,3,4,5};
int[] arr2 = null;//代表这个引用不指向任何对象
System.out.println(arr2);//空指针异常
}
null
的作用类似于 C 语言中的 NULL
(空指针), 都是表示一个无效的内存位置。因此不能对这个内存进行任何读写操作。 一旦尝试读写, 就会抛出:NullPointerException
.
注意: Java 中并没有约定 null
和 0
号地址的内存有任何关联。
JVM 的内存被划分成了几个区域:
程序计数器 (PC Register)
: 只是一个很小的空间, 保存下一条执行的指令的地址.虚拟机栈(JVM Stack)
: 重点是存储局部变量表(当然也有其他信息). 我们刚才创建的 int[] arr
这样的存储地址的引用就是在这里保存.本地方法栈(Native Method Stack)
: 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native
方法的局部变量. 在有些版本的JVM
实现中(例如HotSpot
), 本地方法栈和虚拟机栈是一起的.堆(Heap)
: JVM
所管理的最大内存区域. 使用 new
创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3}
).方法区(Method Area)
: 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域.运行时常量池(Runtime Constant Pool)
: 是方法区的一部分, 存放字面量(字符串常量)与符号引用. (注意 从 JDK1.7
开始, 运行时常量池在堆上).Native 方法:
JVM 是一个基于 C++ 实现的程序. 在 Java 程序执行过程中, 本质上也需要调用 C++ 提供的一些函数进行和操作系统底层进行一些交互. 因此在 Java 开发中也会调用到一些 C++ 实现的函数.这里的 Native 方法就是指这些 C++ 实现的, 再由 Java 来调用的函数.
写一个方法, 将数组中的每个元素都 * 2
/**
* 在原来的数组上扩大2倍
* @param arr
*/
public static void func(int[] arr){
for (int i = 0; i < arr.length; i++) {
arr[i] = 2 * arr[i];
System.out.print(arr + " ");
}
}
/**
* 不改变原数组,将数组扩大2倍,创建一个新的数组用来接收扩大2倍之后的值
* @param arr
* @return
*/
public static int[] func3(int[] arr){
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = 2 * arr[i];
}
return ret;
}
public static void main9(String[] args) {
int[] arr = {
1,2,3,4,5};
func(arr);
int[] ret2 = func3(arr);
System.out.print(Arrays.toString(ret2));
}
/**
* 写一个方法将数组转字符串
* @param arr
* @return
*/
public static String myToString(int[] arr){
if(arr == null){
return null;
}
String ret = "[";
for (int i = 0; i < arr.length; i++) {
ret += arr[i];
if(i != arr.length - 1){
ret += ",";
}
}
ret += "]";
return ret;
}
public static void main10(String[] args) {
int[] arr = {
1,2,3,4,5};
System.out.println(myToString(arr));
}
利用java所提供的Arrays包,也可以实现数组转字符串操作:
import java.util.Arrays
int[] arr = {
1,2,3,4,5,6};
String newArr = Arrays.toString(arr);
System.out.println(newArr);//[1, 2, 3, 4, 5, 6]
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5,6};
System.out.println(maxNum(arr));
}
public static int maxNum(int[] arr){
if(arr == null){
return -1;
}
if(arr.length == 0){
return -1;
}
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if(max < arr[i]){
max = arr[i];
}
}
return max;
}
public static double avg(int[] arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return (double) sum/(double) arr.length;
}
public static void main(String[] args) {
int[] arr = {
1,2,3,4,5,6};
System.out.println(avg(arr));
}
给定一个数组, 再给定一个元素, 找出该元素在数组中的位置,返回数组的下标。
public static int findNum(int[] arr,int key){
for (int i = 0; i < arr.length; i++) {
if(arr[i] == key){
return i;
}
}
return -1;
}
public static void main11(String[] args) {
int[] arr = {
4,5,2,8,44,2,0};
System.out.println(findNum(arr,8));
}
以升序数组为例, 二分查找的思路是先取中间位置的元素, 看要找的值比中间元素大还是小. 如果小, 就去左边找; 否则就去右边找。
public static int binarySearch(int[] arr,int k){
int l = 0;
int r = arr.length - 1;
while (l < r){
int mid = (l + r)/2;
if(arr[mid] > k){
r = mid - 1;
}
else if(arr[mid] < k){
l = mid + 1;
}
else
return mid;
}
return -1;
}
public static void main11(String[] args) {
int[] arr = {
1,2,3,4,5,78,99};
System.out.println(binarySearch(arr,78));
System.out.println(Arrays.binarySearch(arr, 78)); //自带的二分查找
}
public static void main(String[] args) {
int[] arr = {
1,2,3,10,5,6};
System.out.println(isSorted(arr));
}
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
int[] arr = {
3,5,6,1,2,9,0,8,4,7};
boolean flg = false;
for (int i = 0; i < arr.length; i++) {
for (int j = arr.length - 1; j > i; j--) {
if(arr[j] < arr[j - 1]){
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
flg = true;
}
}
if(flg == false){
return;//break;
}
}
System.out.println(Arrays.toString(arr));
}
加上flg
之后,如果数组在排序途中已经有序,可提前结束循环。
冒泡排序性能较低. Java 中内置了更高效的排序算法:
public static void main(String[] args) {
int[] arr = {
9, 5, 2, 7};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
给定一个数组, 将里面的元素逆序排列.
思路:设定两个下标, 分别指向第一个元素和最后一个元素. 交换两个位置的元素.
然后让前一个下标自增, 后一个下标自减, 循环继续即可.
public static void main(String[] args) {
int[] arr = {
1, 2, 3, 4};
reverse(arr);
System.out.println(Arrays.toString(arr));
}
public static void reverse(int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
给定一个整型数组, 将所有的偶数放在前半部分, 将所有的奇数放在数组后半部分.
思路:设定两个下标分别指向第一个元素和最后一个元素.用前一个下标从左往右找到第一个奇数, 用后一个下标从右往左找到第一个偶数, 然后交换两个位置的元素.依次循环即可.
public static void main(String[] args) {
int[] arr = {
1, 2, 3, 4, 5, 6};
transform(arr);
System.out.println(Arrays.toString(arr));
}
public static void transform(int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
// 该循环结束, left 就指向了一个奇数
while (left < right && arr[left] % 2 == 0) {
left++;
}
// 该循环结束, right 就指向了一个偶数
while (left < right && arr[right] % 2 != 0) {
right--;
}
// 交换两个位置的元素
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
public static void main(String[] args) {
int[] arr = {
11,12,13,45,56};
int[] copy = arr.clone();
System.out.println(Arrays.toString(copy));
}
public static void main(String[] args) {
int[] arr = {
11,12,13,45,56};
int[] copy = new int[arr.length];
System.arraycopy(arr,0,copy,0,arr.length);
//arr表示原数组,0表示原数组拷贝的起始地址,copy表示目的数组,0表示目的数组的
//起始位置,arr.length表示要拷贝的数组的长度
System.out.println(Arrays.toString(copy));
}
System.arraycopy(arr,0,copy,0,arr.length);
//arr表示原数组,0表示原数组拷贝的起始地址,copy表示目的数组,0表示目的数组的
//起始位置,arr.length表示要拷贝的数组的长度
public static void main(String[] args) {
int[] arr = {
11,12,13,45,56};
//可以拷贝数组的长度,也可以拷贝数组长度的整数倍,
//拷贝其整数倍之后,后面的值如果不赋初值,其默认都为0.
int[] ret = Arrays.copyOf(arr,arr.length * 2);
//也可以用Arrays.copyOfRange()拷贝数组中得指定长度
int[] ret1 = Arrays.copyOfRange(arr,1,3);
System.out.println(Arrays.toString(ret));
System.out.println(Arrays.toString(ret1));
public static void main(String[] args) {
//定义一个数组长度为10的一维数组
int[] arr = new int[10];
//长度为10的一维数组中的所有元素都填充为99
Arrays.fill(arr,99); //填充
//一维数组中的指定下标元素填充为99
Arrays.fill(arr,2,6,99);
//System.out.println(Arrays.toString(arr));
}
二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组.
语法格式:
数据类型[][] 数组名称 = new 数据类型 [行数][列数] {
初始化数据 };
int[][] arr1 = {
{
1,2,3},{
4,5,6}};
int[][] arr2 = new int[][]{
{
1,2,3},{
4,5,6}};
int[][] arr3 = new int[2][3];
public static void main(String[] args) {
int[][] arr = {
{
1, 2, 3, 4},
{
5, 6, 7, 8},
{
9, 10, 11, 12}
};
}
//二维数组的打印,方法1:
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();//换行
System.out.println("******************");
//二维数组的打印,方法2:
for (int[] ret:arr) {
for (int x:ret) {
System.out.print(x+" ");
}
System.out.println();
}
System.out.println("******************");
//二维数组的打印,方法3:Arrays里面自带的包打印数组
System.out.println(Arrays.deepToString(arr));
}
如果二维数组里面的数字没有全部写完,则打印出来的二维数组相应位置是个空格,而不是0。
如果二维数组的列没有写,可以对它的列重新赋值。
public static void main5(String[] args) {
int[][] arr1 = {
{
1,2},{
4,5,6}};
for (int i = 0; i < arr1.length; i++) {
for (int j = 0; j < arr1[i].length; j++) {
System.out.print(arr1[i][j] + " ");
}
System.out.println();
}
System.out.println("*****************");
//不规则的二维数组
int[][] arr3 = new int[2][];
arr3[0] = new int[3];
arr3[1] = new int[2];
for (int i = 0; i < arr3.length; i++) {
for (int j = 0; j < arr3[i].length; j++) {
System.out.print(arr3[i][j] + " ");
}
System.out.println();
}
}
以上。