Java核心技术卷I (11版)第三章学习(四)
文章目录
数组
声明数组
int[] a = new int[100]; // 声明并初始化一个可以存储100个整数的数组
int[] smallPrimes = {2, 3, 5};
// 匿名数组
new int[] {17, 19, 31};
smallPrimes = new int[] {17, 19, 31}; // 可以使用匿名数组来重新初始化一个数组
// 创建长度为0的数组
new elementType[0];
new elementType[] {};
new int[n]
表示创建一个长度为n
的数组- 若需要扩展数组的大小,则需要使用另一种数组结构——array list
int[] a
和int a[]
都可以定义一个数组变量- Java中有一种创建数组对象并同时提供初始值的简写形式,即
int[] smallPrimes = {2, 3, 5};
。这个语法中不需要使用new
,也不用指定长度 - 匿名数组:会分配一个新数组并填入大括号中的值
- 长度为0的数组与
null
并不相同- 长度为0的数组:它实际上是一个已经实例化的数组对象,但是它没有包含任何元素。在许多编程语言中,你可以对其进行操作,比如获取其长度(结果为0)、遍历(实际上不会执行任何操作,因为没有元素)、或者添加元素等。
null
:在许多编程语言中,null
通常表示一个引用变量没有引用任何对象。也就是说,它不是一个已经实例化的对象,而是一个表示“无”或“不存在”的特殊值。当你尝试在一个null
引用上执行操作时,比如获取长度或遍历,你通常会得到一个空指针异常(NullPointerException
)。
访问数组元素
int[] a = new int[100];
for (int i=0; i<100; i++){
a[i]=i;
}
String[] str = new String[10];
for (int i = 0; i<10; i++){
str[i]="";
}
for (int i=0; i<a.length; i++){
System.out.println(a[i]);
}
- 数组元素的下标从0开始
for each 循环
// 打印数组a的每一个元素,一个元素一行
for (int element : a)
System.out.println(element);
for (variable : collection) statement
:它定义一个变量用于暂存集合中的每一个元素,并执行相应的语句collection
必须是一个数组或者是一个实现了Iterable
接口的类对象(例如ArrayList
)for each
循环语句的循环变量variable
会遍历数组中的每个元素,而不是下标值- 有更简单的方式可以打印数组中的所有值,即利用
Arrays
类的toString
方法。调用Arrays.toString(a)
,返回一个包含数组元素的字符串,例如“[2,3,4,6,7,11]”
,想要打印只需调用System.out.println(Arrays.toString(a));
数组拷贝
int [] luckNumbers = smallPrimes; // smallPrimes是被拷贝的数组
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
- Java 允许将一个数组变量拷贝到另一个数组变量,这时两个变量将引用同一个数组
copyOf
方法的第二个参数是指新数组的长度,这个方法可以用来增加数组长度。- 如果数组元素是数值型,则额外的元素被赋值为
0
;如果数组元素时布尔型,则将赋值false
Java数组与堆栈上C++数组的不同
- C++数组:
- 在C++中,数组可以分配在栈上或堆上。
- 当数组在栈上分配时(例如,
int arr[10];
),数组的大小是固定的,且数组的生命周期与定义它的作用域相同。栈上数组的效率很高,但它们的大小和生命周期是受限的。 - 当数组在堆上分配时(例如,通过
new int[10]
),数组的大小可以在运行时确定,并且数组的生命周期可以通过delete[]
操作符手动管理。堆上分配提供了更大的灵活性,但也需要更多的注意来避免内存泄漏和其他问题。
- Java数组:
- 在Java中,所有数组都是在堆上分配的,并且是通过Java的垃圾回收器自动管理的。
- Java数组的大小是动态的,可以在运行时确定,并且可以通过数组的
length
属性来查询。 - Java数组是对象,它们包含了对实际数据元素的引用和一个表示数组长度的属性。这意味着即使数组本身是对象,它的元素也可以是基本类型或对象引用。
- Java不提供指针算术,因此不能通过类似C++中的指针运算来直接访问数组元素。相反,必须使用数组的索引操作符
[]
来访问元素。
C++ 数组示例
#include <iostream>
int main() {
// 在栈上分配数组
int stackArray[5] = {1, 2, 3, 4, 5};
std::cout << "Stack array element at index 2: " << stackArray[2] << std::endl;
// 在堆上分配数组
int* heapArray = new int[5];
heapArray[0] = 1;
heapArray[1] = 2;
heapArray[2] = 3;
heapArray[3] = 4;
heapArray[4] = 5;
std::cout << "Heap array element at index 2: " << heapArray[2] << std::endl;
// 释放堆上分配的内存
delete[] heapArray;
return 0;
}
在C++中,你可以选择在栈上或堆上分配数组。栈上数组的大小在编译时确定,而堆上数组的大小可以在运行时确定。对于堆上分配的数组,你需要使用new
操作符来分配内存,并在不再需要数组时使用delete[]
操作符来释放内存。
Java 数组示例
public class ArrayExample {
public static void main(String[] args) {
// 在堆上分配数组(Java中唯一的分配方式)
int[] array = {1, 2, 3, 4, 5};
System.out.println("Array element at index 2: " + array[2]);
// 动态地创建和初始化数组
int[] dynamicArray = new int[5];
dynamicArray[0] = 1;
dynamicArray[1] = 2;
dynamicArray[2] = 3;
dynamicArray[3] = 4;
dynamicArray[4] = 5;
System.out.println("Dynamic array element at index 2: " + dynamicArray[2]);
// Java的垃圾回收器会自动处理不再使用的数组内存
}
}
在Java中,所有数组都是在堆上分配的,并且你不需要(也不能)手动管理它们的内存。数组的大小可以在运行时确定,并且Java的垃圾回收器会自动处理不再使用的数组。你不能像在C++中那样进行指针算术来访问数组元素;你必须使用数组的索引操作符[]
。
在Java中,[]
运算符用于访问数组的元素。Java确实在运行时进行了数组的越界检查,如果你尝试访问数组索引超出其实际长度的元素,Java会抛出ArrayIndexOutOfBoundsException
。
关于指针运算,Java确实不支持传统的C/C++风格的指针运算。在C或C++中,你可以通过指针算术来直接访问数组中的下一个元素,比如a + 1
。但在Java中,你不能这样做,因为Java中的数组引用并不是指向数组首元素的指针,而是一个指向数组对象的引用。
java.util.Arrays
类方法概述
java.util.Arrays
类包含了一系列静态方法,用于操作数组(如排序、搜索、复制和填充)。以下是该类中一些常用方法的简要说明:
-
static String toString(xxx[] a)
:
返回一个字符串,表示指定的数组及其元素。数组元素被包含在方括号内,并用逗号分隔。这里的xxx
是数组元素的类型。 -
static xxx[] copyOf(xxx[] original, int newLength)
:
返回一个新数组,它是原始数组的副本,具有指定的新长度。如果新长度大于原始数组的长度,则新数组超出的部分将被填充为该类型的默认值(如0
对于整型,false
对于布尔型)。如果新长度小于原始数组的长度,则新数组将仅包含原始数组的前newLength
个元素。 -
static xxx[] copyOfRange(xxx[] original, int from, int to)
:
返回一个新数组,它是原始数组指定范围的一个副本。新数组包含从索引from
(包含)到索引to
(不包含)的原始数组的元素。原始数组和新数组是独立的,对其中一个数组的修改不会影响另一个数组。如果from
和to
指定的范围超出原始数组的界限,则抛出ArrayIndexOutOfBoundsException
。 -
static void sort(xxx[] a)
:
使用优化的快速排序算法对数组进行排序。排序是按元素的自然顺序进行的,或者根据数组元素的类型实现的Comparable
接口。 -
static int binarySearch(xxx[] a, xxx key)
或static int binarySearch(xxx[] a, int fromIndex, int toIndex, xxx key)
:
使用二分查找算法在有序数组中搜索指定的键。如果找到键,则返回其在数组中的索引;否则返回一个负值,表示应该插入键的位置的-insertionPoint - 1
。第二个版本允许在数组的指定范围内搜索键。 -
static void fill(xxx[] a, xxx val)
:
将指定的值分配给数组的每个元素。此方法可用于快速初始化或重置数组的内容。 -
static boolean equals(xxx[] a, xxx[] a2)
:
如果两个数组的长度相等,并且它们对应的元素都相等,则返回true
;否则返回false
。此方法可用于比较两个数组的内容是否完全相同。
示例
下面是一个综合示例,演示了如何使用 java.util.Arrays
类中的一些方法:
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
// 创建一个整型数组
int[] numbers = {5, 2, 9, 1, 5, 6};
// 使用 toString 方法打印数组内容
System.out.println("Original array: " + Arrays.toString(numbers));
// 使用 sort 方法对数组进行排序
Arrays.sort(numbers);
System.out.println("Sorted array: " + Arrays.toString(numbers));
// 使用 binarySearch 方法查找元素(数组必须已排序)
int index = Arrays.binarySearch(numbers, 5);
System.out.println("Index of 5 (first occurrence): " + index);
// 使用 copyOf 方法创建一个新长度的数组副本
int[] numbersCopy = Arrays.copyOf(numbers, 10);
System.out.println("Copied array (length 10): " + Arrays.toString(numbersCopy));
// 使用 copyOfRange 方法创建一个指定范围的数组副本
int[] rangeCopy = Arrays.copyOfRange(numbers, 1, 4);
System.out.println("Range copy (from index 1 to 3): " + Arrays.toString(rangeCopy));
// 使用 fill 方法填充数组
Arrays.fill(numbers, 0);
System.out.println("Filled array with zeros: " + Arrays.toString(numbers));
// 比较两个数组是否相等
int[] anotherArray = {0, 0, 0, 0, 0, 0};
boolean areEqual = Arrays.equals(numbers, anotherArray);
System.out.println("Are the arrays equal? " + areEqual);
}
}
在这个示例中,我们创建了一个整型数组,并使用 Arrays
类中的不同方法对其进行操作。注意,在使用 binarySearch
方法之前,数组必须是已排序的,否则结果将不可预测。此外,copyOfRange
方法的参数是基于零的索引,并且不包括结束索引所指定的元素。
Java多维数组详解
多维数组,即具有多个维度的数组。在Java中,多维数组实际上是“数组的数组”,即数组的每一个元素本身就是一个数组。最常见的多维数组是二维数组,类似于表格结构,有行和列之分。但实际上,Java支持创建任意维度的数组。
二维数组的声明方式如下:
int[][] twoDArray; // 声明一个二维整型数组
初始化二维数组有两种常见方式:直接指定行数和列数进行默认初始化,或在声明的同时进行显式初始化。例如:
int[][] twoDArray = new int[3][4]; // 创建一个3行4列的二维数组,所有元素默认初始化为0
// 或者在声明的同时进行初始化
int[][] anotherTwoDArray = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
Java还支持不规则二维数组,即每一行的长度可以不同:
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[2];
jaggedArray[1] = new int[3];
jaggedArray[2] = new int[4];
// 或者在声明的同时进行初始化
int[][] anotherJaggedArray = {
{1, 2},
{3, 4, 5},
{6, 7, 8, 9}
};
对于三维及更高维度的数组,声明和初始化方式与二维数组类似,只是增加了额外的维度:
int[][][] threeDArray = new int[2][3][4]; // 创建一个2个3x4的二维数组堆叠起来的三维数组
// 访问三维数组的元素时需要指定三个索引
int value = threeDArray[1][2][3]; // 访问第二个3x4数组的第三行第四列的元素
多维数组的内存布局
在内存中,多维数组实际上是连续存储的。对于二维数组来说,它可以看作是一个“数组的数组”,即第一维(行)的数组元素是指向第二维(列)数组的引用。这种布局方式使得Java能够在内存中高效地存储和访问多维数组的元素。对于更高维度的数组,同样采用类似的布局方式。
笔记目录
Java核心技术卷I (11版)第二章学习
Java核心技术卷I (11版)第三章学习(一)
Java核心技术卷I (11版)第三章学习(二)
Java核心技术卷I (11版)第三章学习(三)