Arrays
public class ArraysTestDrive {
public static void main(String[] args)
{
// []代表这是一个array,并且array中的所有元素都是相同的int型
// 使用new代表这是一个实例化的过程。左右分开看,右边是一个array类型的对象,左边arr是一个reference。
// [5]意味着数组的长度是5,并且不可改变。注意声明完成之后,所有的5个元素都被设为默认值0
int[] arr = new int[5];
// 通过索引器[] + 下标,访问每个数组元素。注意不要越界。
System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] + ", " + arr[4]);
// 初始化方式一:逐个赋值
arr[0] = 2;
arr[1] = 3;
arr[2] = 5;
arr[3] = 7;
arr[4] = 11;
System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] + ", " + arr[4]);
// 初始化方式二:初始化列表
int[] arr2 = {2, 3, 5, 7, 11};
System.out.println(arr2[0] + ", " + arr2[1] + ", " + arr2[2] + ", " + arr2[3] + ", " + arr2[4]);
// 获得数组的长度
int arrLength = arr.length;
System.out.println("The length of arr: " + arrLength);
// 遍历方式一:for循环
for(int i = 0; i < arr.length; i++)
{
arr[i] = 6;
}
System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] + ", " + arr[4]);
// 遍历方式二:for-each循环。 更简洁,但是不具备删除或替换元素的能力。
for(int element : arr)
{
element = 9;
}
System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] + ", " + arr[4]);
// 数组的类型可以是class,意味着数组里的元素都是object。我们说在for-each中不能更改元素,但是对象可以调用更改器。
Person reno = new Person("DingLei", 181, 84.2);
Person pudding = new Person("ChenKexuan", 161, 45);
Person shelly = new Person("HuangYujia", 168, 49);
Person[] personQueue = { reno, pudding, shelly };
System.out.println(personQueue[0].getName() + ", " + personQueue[1].getName() + ", " + personQueue[2].getName());
for(Person person : personQueue)
{
// 调用更改器OK
person.setName("Nobody");
// 替换元素仍然不可以
person = new Person("Somebody", 100, 100);
}
System.out.println(personQueue[0].getName() + ", " + personQueue[1].getName() + ", " + personQueue[2].getName());
// 数组是一个对象,所以传递的是引用
changeArray(arr);
System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] + ", " + arr[4]);
// 数组元素,有可能是基础类型,也有可能是引用类型。注意区别对待。
changePrimitiveElement(arr[0]);
System.out.println(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] + ", " + arr[4]);
changeReferenceElement(personQueue[0]);
System.out.println(personQueue[0].getName() + ", " + personQueue[1].getName() + ", " + personQueue[2].getName());
// 分析算法。在一个有序数组中插入一个数字,插入后使数组仍然是有序的。
int[] arr3 = {2, 4, 6, 8, 10};
// 插入到第一个位置,后面所有元素都要移动,是worst case。
int[] arr4 = insertByOrder(arr3, 1);
// 插入到最后一个位置,没有一个元素需要移动,是best case
int[] arr6 = insertByOrder(arr3, 12); // 2,4,6,8,10,12
// 插入到中间的位置,是average case
int[] arr5 = insertByOrder(arr3, 5); // 2,4,5,6,8,10
// -------------------------- 次元的分割线,以下是二维数组------------------------------
// 二维Array声明
// [][]代表这是一个二维Array,并且具有3行4列
int[][] mat = new int[3][4];
// 初始化方式一:用索引器
// 先初始化第一行
mat[0][0] = 2;
mat[0][1] = 6;
mat[0][2] = 8;
mat[0][3] = 7;
// 再初始化第二行
mat[1][0] = 1;
mat[1][1] = 5;
mat[1][2] = 4;
mat[1][3] = 0;
// 再初始化第三行
mat[2][0] = 9;
mat[2][1] = 3;
mat[2][2] = 2;
mat[2][3] = 8;
// 初始化方式二:用初始化列表,直观明了
int[][] mat2 = {
{2, 6, 8, 7},
{1, 5, 4, 0},
{9, 3, 2, 8},
};
// 我们也可以这样理解,二维Array是行的Array
// mat有3个元素,可以这样想象,mat表示为 { mat[0], mat[1], mat[2] }
int matLen = mat.length;
System.out.println(matLen);
// 其中,mat[0]又是一个标准的一维Array
// mat[0]有4个元素, mat[0]表示为 {2, 6, 8, 7}
int[] firstRow = mat[0];
int firstRowLen = mat[0].length;
System.out.println(firstRowLen);
System.out.println(firstRow[0] + ", " + firstRow[1] + ", " + firstRow[2] + ", " + firstRow[3]);
// 在二维Array中访问到元素的话,必须同时指定行下标和列下标
System.out.println(mat[1][1]);
// 遍历方式一:for循环,row-column模式
// 外层循环,先遍历行。 mat.length代表有多少行
for(int r=0; r < mat.length; r++){
// 内层循环,再遍历列。 mat[r]代表有多少列
for(int c=0; c< mat[r].length; c++){
// 有了行下标和列下标,才能拿到元素
System.out.print(mat[r][c] + ", ");
}
}
System.out.println();
// 遍历方式二:for-each循环,row-column模式
// 外层循环,先遍历行。 注意mat的元素mat[0], mat[1], mat[2],每个元素都是数组,所以类型是int[]
for(int[] row : mat){
// 内层循环,再遍历列。对于每行,例如mat[0],它里面每个元素才是int
for(int element : row){
System.out.print(element + ", ");
}
}
System.out.println();
// 遍历方式三:逐行扫描。要谨记mat的元素是mat[0], mat[1], mat[2],每个元素都是一个数组。
for(int r=0; r < mat.length; r++){
printArray(mat[r]);
}
System.out.println();
}
public static void printArray(int[] array)
{
for(int element : array)
{
System.out.print(element + ", ");
}
}
public static void changeArray(int[] array)
{
for(int i = 0; i < array.length; i++)
{
array[i] += 3;
}
}
public static void changePrimitiveElement(int element)
{
element += 3;
}
public static void changeReferenceElement(Person element)
{
element.setName("Anybody");
}
public static int[] insertByOrder(int[] array, int newElement)
{
// 因为要插入一个数字,所以重新建一个数组扩大1个长度,并将原来的元素都拷贝过来。
// 注意newArray的最后一个元素没有做初始化,这里会设置默认值为0.
int arrLen = array.length;
int[] newArray = new int[arrLen + 1];
for(int i = 0; i < arrLen; i++)
{
newArray[i] = array[i];
}
// 从第一个元素开始遍历,找到新元素应该插入的位置
int newArrLen = newArray.length;
int newPosition = 0;
while(newPosition < newArrLen - 1 && newElement > newArray[newPosition])
{
newPosition++;
}
// 找到合适的位置后
// 该位置之后的元素都要往后移动一格
// 该位置要填入新元素
// 该位置之前的元素不需要做任何处理
for(int i = newArrLen - 1; i > newPosition; i--)
{
newArray[i] = newArray[i - 1];
}
newArray[newPosition] = newElement;
// 遍历新数组,输出结果
for(int element : newArray)
{
System.out.print(element + ", ");
}
System.out.println();
return newArray;
}
}
ArrayList
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ArrayListTestDrive {
@SuppressWarnings("deprecation")
public static void main(String[] args)
{
// 声明,引用类型是接口List,创建出来的对象是ArrayList。这是为了多态。
// 声明时,不需要指定长度,因为ArrayList是动态调整的。
List<Person> personQueue = new ArrayList<Person>();
// add方法:往ArrayList中添加元素
Person reno = new Person("DingLei", 181, 84.2);
Person pudding = new Person("ChenKexuan", 161, 45);
Person shelly = new Person("HuangYujia", 168, 49);
personQueue.add(reno);
personQueue.add(pudding);
personQueue.add(shelly);
// get方法:访问其中某个元素,索引值的规则跟Array一样
Person firstPerson = personQueue.get(0);
Person secondPerson = personQueue.get(1);
Person thirdPerson = personQueue.get(2);
System.out.println(firstPerson.getName() + ", " + secondPerson.getName() + ", " + thirdPerson.getName());
// size方法:返回ArrayList的长度
int listSize = personQueue.size();
System.out.println(listSize);
// set方法:更改其中某个元素
Person maxG = new Person("Guhaowei", 170, 70);
personQueue.set(0, maxG);
System.out.println(personQueue.get(0).getName() + ", " + personQueue.get(1).getName() + ", " + personQueue.get(2).getName());
// add方法重载:可以在任意位置添加元素。
// 对比一下Array是怎么添加元素的,是不是特别简单?
personQueue.add(0, reno);
System.out.println(personQueue.get(0).getName() + ", " + personQueue.get(1).getName() + ", " + personQueue.get(2).getName()+ ", " + personQueue.get(3).getName());
// remove方法:删除任意位置的元素
personQueue.remove(1);
System.out.println(personQueue.get(0).getName() + ", " + personQueue.get(1).getName() + ", " + personQueue.get(2).getName());
// iterator方法:获得一个迭代器
Iterator<Person> personItr = personQueue.iterator();
// ----------------------------华丽的分割线,以下讨论Iterator -----------------------------------
// Iterator属于标准包 java.util
// 它是一个迭代器,可以遍历ArrayList。通过hasNext和next搭配
while(personItr.hasNext())
{
Person person = personItr.next();
System.out.print(person.getName() + ", ");
}
System.out.println();
// 它始终保持着当前的位置,现在已经遍历完了,当前位置在数组结尾
// 如果我想重新遍历,再次调用personItr.hasNext()是不行的,因为肯定会返回false
// 所以必须重新声明一次Iterator
personItr = personQueue.iterator();
// 其实for-each的背后机制,就是靠Iterator来实现遍历的。不断判断hasNext并获得next
for(Person person : personQueue)
{
System.out.print(person.getName() + ", ");
// 但是for-each不允许删除元素,以下会报错
// personQueue.remove(person);
}
System.out.println();
// 如果把for-each翻译为Iterator的方式完全没问题,并且同时也具备了删除元素的能力
while(personItr.hasNext())
{
personItr.next();
personItr.remove();
}
System.out.println(personQueue.size());
// ----------------------------华丽的分割线,以下讨论装箱和拆箱 -----------------------------------
// ArrayList必须存储对象,而不是基础类型(比如double和int)
// 所以不能声明为 ArrayList<int> 而是 ArrayList<Integer>
List<Integer> list = new ArrayList<Integer>();
// 回顾一下,int是基础类型,Integer是它对应的引用类型
// int声明出来的是一个基础数值
int intValue = 1;
// Integer声明出来的是一个引用对象
Integer intObject = new Integer(1);
// 把int转换为Integer的过程,就是装箱。形象的理解为把一个基础数值装到对象这个箱子里。我们也称Integer是Wrapper Class
Integer intObject2 = new Integer(intValue);
// 把Integer转换为int的过程,就是拆箱。形象的理解为把对象箱子里的基础数值取出来。
int intValue2 = intObject2.intValue();
// 类比一下把大象装进冰箱里总共分三步
// ①把冰箱门打开 ②把大象装进去 ③把冰箱门关上
// ①把对象创建出来 ②把基础数值装进去对象 ③把对象引用赋给变量
// 为什么要费劲的做装箱、拆箱这个事情呢?
// 装箱用途一:为了使用ArrayList,前面已经讲了,ArrayList必须存储对象。
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(3));
// 装箱用途二:为了配合Generic,所有出现<E>的地方,E必须用引用类型来指定。
// 当然,ArrayList就是一个Generic的应用,Java中还有更多使用Generic的class。
// 装箱用途三:为了调用一些方法
Double doubleObj = new Double(1.12);
boolean b1 = doubleObj.equals(Double.NaN); // 相等判断
int cr = doubleObj.compareTo(Double.MAX_VALUE); // 比较大小
String str = doubleObj.toString(); // 转化为字符串
// 自动装箱与自动拆箱
// 简化刚才的用途一,语法上允许直接填入基础数值。
// 但是请注意,并不是说ArrayList可以填入基础数值,而是说基础数值自动装箱为对象了,这个过程是自动做的,就是为了工程师方便。
list.add(1);
list.add(2);
list.add(3);
// 同理,当调用get方法时,原本应该获取到Integer对象,但是你可以直接用int,因为这里也自动拆箱了。
Integer int1 = list.get(0);
int int2 = list.get(0);
// 最后,请注意,装箱和拆箱是有性能损失的。每一次装箱都是平白无故做了装大象的三个步骤。
// 所以处理数值时,不要用ArrayList,而是用原生的Array。
}
}
String
public class StringTestDrive {
public static void main(String[] args) {
// String是一个对象,我们通常把String对象称为"字符串"
// String的作用就是表示一句话,通常用来显示在界面上。在我们学习课程中,通常用来配合System.out.print输出结果。
// 双引号是字符串的标志,当被双引号包括起来,我们就意识到这是一个字符串
String s1 = "Hello World";
System.out.println(s1);
// 字符串可以不包含任何字符串,我们称为空字符串,但记住它仍然是字符串,而不是空引用null
String s2 = "";
System.out.println(s2);
// 字符串可以包含转义字符,回想我们之前学的 \n, \", \\
String s3 = "I must\n go home";
System.out.println(s3);
// 声明方式一:声明一个对象最典型的方式 - new
String s4 = new String("abc");
// 声明方式二:语法糖,可以像一个基础类型一样声明,这是最常用的方式
String s5 = "abc";
// 字符串连接一:使用+连接两个字符串
String s6 = "Hello " + "World";
System.out.println(s6);
// 字符串连接二:连接字符串和任意对象。
// 这里会自动调用对象的 toString方法,所以实际上还是两个字符串连接。
Person reno = new Person("Reno", 181, 84.7);
String s7 = "Hello " + reno;
System.out.println(s7);
// 字符串连接三:连接字符串和基础数值。
// 这里会将基础数值装箱变为对象,然后自动调用toString方法,所以实际上还是两个字符串连接。
int someInt = 12345;
String s8 = "Hello " + someInt;
System.out.println(s8);
// 注意:做字符串连接的前提是至少有一个为字符串,另一个才会自动调用toString方法。
// 如果两个变量中没有一个字符串,是没法连接的。
// ------------------------ 华丽的分割线,以下讨论String的数组特性---------------------
// 其实可以把String当做一个数组来看待,数组的元素是字符 - char类型
String s9 = "ABCDEFG";
char[] s10 = {'A','B','C','D','E','F','G'};
// 所以数组的一些特性就可以帮助理解String的方法
// length方法:返回String的长度,也就是字符的个数
int strLen = s9.length();
System.out.println(strLen);
// indexOf方法:返回在String中的位置
// A在第1个位置,所以索引值是0
int strIndex = s9.indexOf("A");
System.out.println(strIndex);
// CDE从第3个位置,所以索引值是2
int strIndex2 = s9.indexOf("CDE");
System.out.println(strIndex2);
// 虽然有FG,但是没有FGH,所以找不到位置,返回-1
int strIndex3 = s9.indexOf("FGH");
System.out.println(strIndex3);
// subString方法:从String中截断一段subString
// 重载一:从某个位置截到尾
// 索引是3,表示从第4个位置开始截取,所以返回DEFG
String subStr1 = s9.substring(3);
System.out.println(subStr1);
// 重载二:从某个位置开始,到某个位置结束。 注意第2个参数表示"第一个你不想要的字符"
// 第7个位置不想要,也就是截取第4个位置~第6个位置,返回DEF
String subStr2 = s9.substring(3, 6);
System.out.println(subStr2);
// 比较两个字符串:从左到右,逐个字符比较。
// 返回负数、0、正数,这个数字本身并无意义,主要是用数字的正负性来排序。
// null < 0 < 1 < ... < 9 < A < B < .... < Z < a < b < ....z
// 第一个字符 W > H,所以"World" > "Hello",所以result1 > 0
int result1 = "World".compareTo("Hello");
System.out.println(result1);
// 第一个字符相等,第二个字符 E < e,所以"HELLO" < "Hello",所以result1 < 0
int result2 = "HELLO".compareTo("Hello");
System.out.println(result2);
// 前四个字符Hell都相等,第五个字符 null < o,所以"Hell" > "Hello",所以 result1 < 0
int result3 = "Hell".compareTo("Hello");
System.out.println(result3);
// 相等性判断:应当和比较的规则保持一致。 其实比较=0就意味着相等,<0或者>0都意味着不相等。
boolean bool1 = "HELLO".equals("Hello");
System.out.println(bool1);
boolean bool2 = "Hello".equals("Hello");
System.out.println(bool2);
}
}
把数组作为参数传入
把数组元素作为参数传入 - 元素是基本类型
把数组元素作为参数传入 - 元素是引用类型
往数组插入一个元素
Collection API继承层次图
Array vs. ArrayList
二维数组
装箱
String的不可变性