二进制
二进制,是计算技术中广泛采用的一种数制,由德国数理哲学大师莱布尼茨于 1679 年
发明。二进制数据是用 0 和 1 两个数码来表示的数。它的基数为 2,进位规则是“逢二进
一”。
数字计算机只能识别和处理由‘0’.‘1’符号串组成的代码。其运算模式正是二进制。
二进制对应两种状态,广泛应用于电子科学。比如:可以对应电子器件的开关状态、对
应信号电压状态(+5V 等价于逻辑"1",0V 等价于逻辑"0")、对应卡带是否打孔状态、电磁
存储(磁体状态:南为 0,北为 1)等等
二进制和十进制的转化
在线进制转化的工具: https://tool.lu/hexconvert/
二进制和十进制数的对应
十进制数 | 二进制 | 十六进制 |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 10 | 2 |
3 | 11 | 3 |
4 | 100 | 4 |
5 | 101 | 5 |
6 | 110 | 6 |
7 | 111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | a |
11 | 1011 | b |
12 | 1100 | c |
13 | 1101 | d |
14 | 1110 | e |
15 | 1111 | f |
十进制转二进制
十进制整数转换为二进制整数采用"除 2 取余,逆序排列"法。
二进制转十进制
二进制转十进制采用“权相加法”
注释
单行注释
单行注释使用“//”
开头。
多行注释
多行注释以“/*”
开头以“*/”
结尾。注意,多行注释不能嵌套使用。
文档注释
文档注释以“/**”
开头以“*/”
结尾,注释中包含一些说明性的文字及一些
JavaDoc 标签(后期写项目时,可以生成项目的 API)
标识符和关键字
标识符
标识符是用来给变量、类、方法以及包进行命名的。4 大规则:
- 必须以字母、下划线_、美元符号$开头。
- 其它部分可以是字母、下划线“_”、美元符“$”和数字的任意组合。
- 大小写敏感,且长度无限制。
- 不可以是 Java 的关键字。
标识符的使用规范
- 表示类名的标识符:每个单词的首字母大写,如 Man, GoodMan
- 表示方法和变量的标识符:第一个单词小写,从第二个单词开始首字母大写,我们称之为“驼峰原则”,如 eat(), eatFood()
关键字
abstract | assert | boolean | break | byte | case |
catch | char | class | const | continue | default |
do | double | else | extends | final | finally |
float | for | goto | if | implements | import |
instanceof | int | interface | long | native | new |
null | package | private | protected | public | return |
short | static | strictfp | super | switch | synchronized |
this | throw | throws | transient | try | void |
volatile | while |
数据类型
Java 数据类型分为两大类:基本数据类型(primitive data type)和引用数据类型(reference data type)。
注意 : 引用数据类型的大小统一为 4 个字节,记录的是其引用对象的地址!
整型
类型 | 占用存储空间 | 表数范围 |
---|---|---|
byte | 1 字节 | -27 ~ 27-1(-128~127) |
short | 2 字节 | -215 ~ 215-1 (-32768~32767) |
int | 4 字节 | -231 ~ 231-1 (-2147483648~2147483647) 约 21 亿 |
long | 8 字节 | -263 ~ 263-1 |
Java 语言整型常量的四种表示形式
- 十进制整数,如:99, -500, 0
- 八进制整数,要求以 0 开头,如:015
- 十六进制数,要求 0x 或 0X 开头,如:0x15
- 二进制数,要求 0b 或 0B 开头,如:0b01110011
Java 语言的整型常数默认为 int 型,声明 long 型常量可以后加‘ l ’或‘ L ’ 。
浮点型(Floating Point Number)
类型 | 占用存储空间 | 表数范围 |
---|---|---|
float | 4 字节 | -3.403E38~3.403E38 |
double | 8 字节 | -1.798E308~1.798E308 |
-
float 类型又被称作单精度类型,尾数可以精确到 7 位有效数字。
-
double 表示这种类型的数值精度约是 float 类型的两倍,又被称作双精度类型,绝大部
分应用程序都采用 double 类型。 -
Java 浮点类型常量有两种表示形式
(1) 十进制数形式: 例: 3.14 314.0 0.314
(2) 科学记数法形式 例:3.14e0 3.14E2 3.14E-1 -
浮点型不精确,不要用于比较
浮点数存在舍入误差,数字不能精确表示。浮点数适合普通的科学和工程计算,精度足够; 但不适合精度要求非常高的商业计算,这时候要使用 BigDecimal 进行运算和比较。
-
浮点常量默认类型是 double,要改成 float 可以后面加 F 或 f
字符型
转义字符
转义符 | 含义 | Unicode 值 |
---|---|---|
\b | 退格(backspace) | \u0008 |
\n | 换行 | \u000a |
\r | 回车 | \u000d |
\t | 制表符(tab) | \u0009 |
\“ | 双引号 | \u0022 |
\‘ | 单引号 | \u0027 |
\ | 反斜杠 | \u005c |
注意事项
以后我们学的 String 类,其实是字符序列(char sequence), 本质是 char 字符组成的数组。
布尔型(boolean)
- boolean 类型有两个常量值,true 和 false。
- 在内存中占一个字节或 4 个字节,不可以使用 0 或非 0 的整数替代 true 和 false ,
这点和 C 语言不同。
JVM 规范指出 boolean 当做 int 处理,也就是 4 字节,boolean 数组当做 byte 数组处理,这样我
们可以得出 boolean 类型占了单独使用是 4 个字节,在数组中是确定的 1 个字节。
变量和常量
变量(variable)
变量的本质
- 变量本质上就是代表一个”可操作的存储空间”,空间位置是确定的,但是里面放置
什么值不确定。 - 可通过变量名来访问“对应的存储空间”,从而操纵这个“存储空间”存储的值。
- Java 是一种强类型语言,每个变量都必须声明其数据类型。变量的数据类型决定了
变量占据存储空间的大小。 比如,int a=3; 表示 a 变量的空间大小为 4 个字节。
变量的声明
double salary;
long earthPopulation;
int age
不同数据类型的常量会在内存中分配不同的空间,如图所示。
变量的分类和作用域
变量有三种类型:局部变量、成员变量(也称为实例变量)和静态变量。
类型 | 声明位置 | 从属于 | 生命周期(作用域) |
---|---|---|---|
局部变量 | 方法或语句块内部 | 方法/语句块 | 从声明位置开始,直到方法或语句块执行完毕,局部变量消失 |
成员变量(实例变量) | 类内部,方法外部 | 对象 | 对象创建,成员变量也跟着创建。对象消失,成员变量也跟着消失; |
静态变量(类变量) | 类内部,static 修饰 | 类 | 类被加载,静态变量就有效;类被卸载,静态变量消失。 |
局部变量(local variable)
方法或语句块内部定义的变量。生命周期是从声明位置开始到到方法或语句块执行完毕
为止。局部变量在使用前必须先声明、初始化(赋初值)再使用。
成员变量(也叫实例变量 member variable)
方法外部、类的内部定义的变量。从属于对象,生命周期伴随对象始终。如果不自行初
始化,它会自动初始化成该类型的默认初始值。
静态变量(类变量 static variable)
使用 static 定义。 从属于类,生命周期伴随类始终,从类加载到卸载。
常量(Constant)
在 Java 语言中,用关键字 final 来定义一个常量。常量一旦被初始化后不能再更改。
运算符(operator)
计算机的基本用途就是执行数学运算,Java 提供了一套丰富的运算符来操作变量。
算术运算符
- +,-,*,/,%属于二元运算符。%是取模运算符,就是我们常说的求余数操作。
- 算术运算符中++(自增),–(自减)属于一元运算符。
二元运算符的运算规则:
- 整数运算:
- 如果两个操作数有一个为 long, 则结果也为 long。
- 没有 long 时,结果为 int。即使操作数全为 short,byte,结果也是 int。
- 浮点运算:
- 如果两个操作数有一个为 double,则结果为 double。
- 只有两个操作数都是 float,则结果才为 float。
- 取模运算:
- 其操作数可以为浮点数,一般使用整数,结果是“余数”,“余数”符号和左边操
作数相同,如:7%3=1,-7%3=-1,7%-3=1。
- 其操作数可以为浮点数,一般使用整数,结果是“余数”,“余数”符号和左边操
赋值及其扩展赋值运算符
运算符 | 用法举例 | 等效的表达式 |
---|---|---|
+= | a += b | a = a+b |
-= | a -= b | a = a-b |
*= | a *= b | a = a*b |
/= | a /= b | a = a/b |
%= | a %= b | a = a%b |
关系运算符
关系运算符用来进行比较运算。关系运算的结果是布尔值:true/false;
运算符 | 含义 | 示例 |
---|---|---|
== | 等于 | a==b |
!= | 不等于 | a!=b |
> | 大于 | a>b |
< | 小于 | a<b |
>= | 大于或等于 | a>=b |
<= | 小于或等于 | a<=b |
注意事项
=是赋值运算符,而真正的判断两个操作数是否相等的运算符是==。
==、!= 是所有(基本和引用)数据类型都可以使用。
> 、>=、 <、 <= 仅针对数值类型(byte/short/int/long,float/double 以及
char)。
逻辑运算符
位运算符
注意
&和|既是逻辑运算符,也是位运算符。如果两侧操作数都是 boolean 类型,就作为逻辑
运算符。如果两侧的操作数是整数类型,就是位运算符。
不要把“^”当做数学运算“乘方”,是“位的异或”操作。
条件运算符
x 为 boolean 类型表达式,先计算 x 的值,若为 true,则整个运算的结果为表达式 y 的
值,否则整个运算结果为表达式 z 的值。
运算符优先级的问题
数据类型的转换
除了 boolean 类型之外的七种类型是可以自动转化的。
自动类型转换
自动类型转换指的是容量小的数据类型可以自动转换为容量大的数据类型。如图 所示,黑色的实线表示无数据丢失的自动类型转换,而虚线表示在转换时可能会有精度的损失。
可以将整型常量直接赋值给 byte、 short、 char 等类型变量,而不需要进行强制类型
转换,只要不超出其表数范围即可。
强制类型转换
强制类型转换,又称为造型(cast),用于强制转换数值的类型,可能损失精度。
注意: 不能在布尔类型和任何数值类型之间做强制类型转换
基本类型转化时常见错误和问题
- 操作比较大的数时,要留意是否溢出,尤其是整数操作时。
- L 和 l 的问题:
(1) 不要命名名字为 l 的变量,字母 l 容易和数字 1 混淆。
(2) long 类型使用大写 L,不要用小写 l。
【示例】类型转换常见问题一
int money = 1000000000; //10亿
int years = 20;
//返回的total是负数,超过了int的范围
int total = money*years;
System.out.println("total="+total);
//返回的total仍然是负数。默认是int,因此结果会转成int值,再转成long。但是已经发
生//了数据丢失
long total1 = money*years;
System.out.println("total1="+total1);
//返回的total2正确:先将一个因子变成long,整个表达式发生提升。全部用long来计
算。
long total2 = money*((long)years);
System.out.println("total2="+total2);
运行结果如图所示。
控制语句
顺序结构
顺序结构是一种基本的控制结构,它按照语句出现的顺序执行操作。
代表“先执行 a,再执行 b”的逻辑。
条件判断结构(选择结构)
代表“如果…,则…”的逻辑。
条件判断结构有:if 结构和 switch 结构。而 if 结构又可以分为 if 单分支结构、if-else 双分支结构、if-else if-else 多分支结构。
if 单分支结构
语法结构:
if(布尔表达式){
语句块
}
if-else 双分支结构
语法结构:
if(布尔表达式){
语句块 1
}else{
语句块 2
}
当布尔表达式为真时,执行语句块 1,否则,执行语句块 2。也就是 else 部分。
if-else if-else 多分支结构
语法结构:
if(布尔表达式 1) {
语句块 1;
} else if(布尔表达式 2) {
语句块 2;
}……
else if(布尔表达式 n){
语句块 n;
} else {
语句块 n+1;
}
当布尔表达式 1 为真时,执行语句块 1;否则,判断布尔表达式 2,当布尔表达式 2 为真时,执行语句块 2;否则,继续判断布尔表达式 3·;如果 1~n 个布尔表达式均判定为假时,则执行语句块 n+1,也就是 else 部分。
switch 语句
switch 多分支结构(多值情况)
语法结构:
switch (表达式) {
case 值 1:
语句块 1;
[break];
case 值 2:
语句块 2;
[break];
… … … … …
[default:
默认语句块;]
}
- switch 会根据表达式的值从相匹配的 case 标签处开始执行,一直执行到 break 处或者
是 switch 的末尾。如果表达式的值与任一 case 值不匹配,则进入 default 语句。 - switch 中表达式的值,是 int(byte、short、char 也可,long 不行)、枚举,字符串。
循环结构
循环结构分两大类,一类是当型,一类是直到型。
- 当型:
当布尔表达式条件为 true 时,反复执行某语句,当布尔表达式的值为 false 时才停止循环,比如:while 与 for 循环。 - 直到型:
先执行某语句, 再判断布尔表达式,如果为 true,再执行某语句,如此反复,直到布尔表达式条件为 false 时才停止循环,比如 do-while 循环。
while 循环
语法结构:
while (布尔表达式) {
循环体;
}
- 在循环刚开始时,会计算一次“布尔表达式”的值,若条件为真,执行循环体。而对于后
来每一次额外的循环,都会在开始前重新计算一次。 - 语句中应有使循环趋向于结束的语句,否则会出现无限循环–––"死"循环。
do-while 循环
语法结构:
do {
循环体;
} while(布尔表达式) ;
do-while 循环结构会先执行循环体,然后再判断布尔表达式的值,若条件为真,执行
循环体,当条件为假时结束循环。do-while 循环的循环体至少执行一次。
for循环
语法结构:
for (初始表达式; 布尔表达式; 迭代因子) {
循环体;
}
初始化部分设置:循环变量的初值
条件判断部分为:布尔表达式
迭代因子:控制循环变量的增减
for 循环在执行条件判定后,先执行的循环体部分,再执行步进。
嵌套循环
循环语句内部,再写一个或多个循环,称为嵌套循环。一般工作中多见的就是两层。
【示例】嵌套循环
public class Test14 {
public static void main(String args[ ]) {
for (int i=1; i <=5; i++) {
for(int j=1; j<=5; j++){
System.out.print(i+" ");
}
System.out.println();
}
}
}
执行结果如图所示:
break 语句和 continue 语句
- break 用于强行退出整个循环
- continue 用于结束本次循环,继续下一次
- continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// 根据用户输入的月份,输出当月的天数,不考虑平年闰年
System.out.println("请输入月份:");
int month = input.nextInt();
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
System.out.println(month + "月共有31天!");
break;
case 4:
case 6:
case 9:
case 11:
System.out.println(month + "月共有30天!");
break;
case 2:
System.out.println(month + "月共有28天!");
break;
default:
System.out.println("您输入的月份有误!");
break;
}
方法
语句块
语句块(也叫复合语句)。语句块中定义的变量只能用于自己,外部不能使用。
语句块可以使用外部的变量,而外部不能使用语句块的变量;
方法
- 方法(method):一段用来完成特定功能的代码片段,类似于其它语言的函数(function)。
- 方法用于定义该类或该类的实例的行为特征和功能实现。
- 面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。
- 面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
方法声明格式:
[修饰符 1 修饰符 2 …] 返回值类型 方法名(形式参数列表){
Java 语句;… … …
}
方法的调用方式:
普通方法 对象名.方法名(实参列表)
静态方法 类名.方法名(实参列表)
方法的详细说明
形式参数:在方法声明时用于接收外界传入的数据。(方法定义时)
实参:调用方法时实际传给方法的数据。 (方法调用时)
返回值:执行完毕后,返还给调用它的环境的数据。
返回值类型:事先约定的返回值的数据类型,如无返回值,则为 void。
方法的重载(overload)
重载: 一个类中可以定义多个名称相同,但形式参数列表不同的方法。
注意: 重载的方法,实际是完全不同的方法,只是名称相同而已
构成方法重载的条件:
- 形参列表不同的含义:形参类型、形参个数、形参顺序不同
- 只有返回值不同不构成方法的重载
如:int a(String str){}与 void a(String str){}不构成方法重载 - 只有形参的名称不同,不构成方法的重载
如:int a(String str){}与 int a(String s){}不构成方法重载
递归结构
- 递归是一种常见的算法思路,在很多算法中都会用到。比如:深度优先搜索(DFS:Depth First Search)等。
- 递归的基本思想就是“自己调用自己”
递归结构包括两个部分:
定义递归头。 解决:什么时候不调用自身方法。如果没有头,将陷入死循环,也就
是递归的结束条件。
递归体。 解决:什么时候需要调用自身方法。
【示例】使用递归求 n!
public class Test22 {
public static void main(String[ ] args) {
long d1 = System.currentTimeMillis();
factorial(10);
long d2 = System.currentTimeMillis();
System.out.printf("递归费时:"+(d2-d1)); //耗时:32ms
}
/** 求阶乘的方法*/
static long factorial(int n){
if(n==1){//递归头
return 1;
}else{//递归体
return n*factorial(n-1);//n! = n * (n-1)!
}
}
}
执行结果如图所示:
递归的缺陷
算法简单是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。
数组和数据存储
数组的定义
数组是相同类型数据的有序集合。其中,每一个数据称作一个元素,每个元素可以通过一个
索引(下标)来访问它们。数组的四个基本特点:
- 长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素的类型必须是相同类型,不允许出现混合类型。
- 数组类型可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也是对象,数组中的元素相当于对象的属性!
创建数组和初始化
type[ ] arr_name; //方式一
type arr_name[ ]; //方式二
注意事项
声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM 才分配空间,这时才与长度有关。
声明一个数组的时候并没有数组真正被创建。
构造一个数组,必须指定长度。
【示例】创建基本类型一维数组
public class Test {
public static void main(String args[ ]) {
int[ ] s; // 声明数组;
s = new int[10]; // 给数组分配空间;
for (int i = 0; i < 10; i++) {
s[i] = 2 * i + 1;//给数组元素赋值; 数组是对象,数组中的元素就是对象的属性
System.out.println(s[i]);
}
}
}
【示例】创建引用类型一维数组
class Man{
private int age;
private int id;
public Man(int id,int age) {
super();
this.age = age;
this.id = id;
}
}
public class AppMain {
public static void main(String[ ] args) {
Man[ ] mans; //声明引用类型数组;
mans = new Man[10]; //给引用类型数组分配空间;
Man m1 = new Man(1,11);
Man m2 = new Man(2,22);
mans[0]=m1;//给引用类型数组元素赋值;
mans[1]=m2;//给引用类型数组元素赋值;
}
}
初始化
数组的初始化方式总共有三种:静态初始化、动态初始化、默认初始化。
1.静态初始化
除了用 new 关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。
【示例】数组的静态初始化
int [ ] a = { 1, 2, 3 };// 静态初始化基本类型数组;
Man[ ] mans = { new Man(1, 1), new Man(2, 2) };// 静态初始化引用类型数组;
2.动态初始化
数组定义与为数组元素分配空间并赋值的操作分开进行。
【示例】数组的动态初始化
int[ ] a1 = new int[2];//动态初始化数组,先分配空间;
a1[0]=1;//给数组元素赋值;
a1[1]=2;//给数组元素赋值;
3.数组的默认初始化
数组是对象,它的元素相当于对象的属性;每个元素也按照属性的方式被默认初始化。
【示例】数组的默认初始化
int a2[ ] = new int[2]; // 默认值:0,0
boolean[ ] b = new boolean[2]; // 默认值:false,false
String[ ] s = new String[2]; // 默认值:null, null
数组常见操作
- 遍历指的就是“通过循环遍历数组的所有元素”。
- 拷贝指的是将某个数组的内容拷贝到另一个数组中
数组的遍历
数组元素下标的合法区间:[0, length-1]。我们可以通过下标来遍历数组中的元素,遍历时可以读取元素的值或者修改元素的值。
【示例】使用循环初始化和遍历数组
public class Test {
public static void main(String[ ] args) {
int[ ] a = new int[4];
//初始化数组元素的值
for(int i=0;i<a.length;i++){
a[i] = 100*i;
}
//读取元素的值
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
}
数组的拷贝
System.arraycopy(object src,int srcpos,object dest, int destpos,int length)该方法可以将 src 数组里的元素值赋给 dest 数组的元素,其中 srcpos 指定从 src 数组的第几个元素 开始赋值,length 参数指定将 src 数组的多少个元素赋给 dest 数组的元素
【示例】数组的拷贝】
public class Test {
public static void main(String args[ ]) {
String[ ] s = {"阿里","尚学堂","京东","搜狐","网易"};
String[ ] sBak = new String[6];
System.arraycopy(s,0,sBak,0,s.length);
for (int i = 0; i < sBak.length; i++) {
System.out.print(sBak[i]+ "\t");
}
}
}
java.util.Arrays 类
Arrays 类包含了:排序、查找、填充、打印内容等常见的数组操作。
【示例】使用 Arrays 类输出数组中的元素
import java.util.Arrays;
public class Test {
public static void main(String args[ ]) {
int[ ] a = { 1, 2 };
System.out.println(a); // 打印数组引用的值;
System.out.println(Arrays.toString(a)); // 打印数组元素的值;
}
}
【示例】使用 Arrays 类对数组元素进行排序一
import java.util.Arrays;
public class Test {
public static void main(String args[ ]) {
int[ ] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println(Arrays.toString(a));
}
}
【示例】使用 Arrays 类实现二分法查找法
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
int[ ] a = {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.sort(a); //使用二分法查找,必须先对数组进行排序;
System.out.println(Arrays.toString(a));
//返回排序后新的索引位置,若未找到返回负数。
System.out.println("该元素的索引:"+Arrays.binarySearch(a, 12));
}
}
【示例】使用 Arrays 类对数组进行填充
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
int[ ] a= {1,2,323,23,543,12,59};
System.out.println(Arrays.toString(a));
Arrays.fill(a, 2, 4, 100); //将2到4索引的元素替换为100;
System.out.println(Arrays.toString(a));
}
}
多维数组
多维数组可以看成以数组为元素的数组。
【示例】二维数组的声明
public class Test {
public static void main(String[ ] args) {
// Java中多维数组的声明和初始化应按从低维到高维的顺序进行
int[ ][ ] a = new int[3][ ];
a[0] = new int[2];
a[1] = new int[4];
a[2] = new int[3];
// int a1[ ][ ]=new int[ ][4];//非法
}
}
【示例】二维数组的静态初始化
public class Test {
public static void main(String[ ] args) {
int[ ][ ] a = { { 1, 2, 3 }, { 3, 4 }, { 3, 5, 6, 7 } };
System.out.println(a[2][3]);
}
}
Comparable 接口
多个对象做比较,就要有“比较规则”,然后实现排序。
事实上,java 中排序算法的底层也依赖 Comparable 接口。
Comparable 接口中只有一个方法:
public int compareTo(Object obj) obj
为要比较的对象方法中,将当前对象和 obj 这个对象进行比较,如果大于返回 1,等于返回 0,小于返回-1. (此处的 1 也可以是正整数,-1 也可以是负整数)。 compareTo 方法的代码也比较固定:
public int compareTo(Object o) {
Man man = (Man) o;
if (this.age < man.age) {
return -1;
}
if (this.age > man.age) {
return 1;
}
return 0;
}
【测试 Comparable 接口】使用 Arrays 类对数组元素进行排序二
import java.util.Arrays;
public class Test {
public static void main(String[ ] args) {
Man[ ] msMans = { new Man(3, "a"), new Man(60, "b"), new Man(2, "c") };
Arrays.sort(msMans);
System.out.println(Arrays.toString(msMans));
}
}
class Man implements Comparable {
int age;
int id;
String name;
public Man(int age, String name) {
super();
this.age = age;
this.name = name;
}
public String toString() {
return this.name;
}
public int compareTo(Object o) {
Man man = (Man) o;
if (this.age < man.age) {
return -1;
}
if (this.age > man.age) {
return 1;
}
return 0;
}
}
常见算法
冒泡排序算法
冒泡排序算法重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就
把他们交换过来,这样越大的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运作如下:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最
后的元素应该会是最大的数。 - 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
【示例】冒泡排序的优化算法
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] array ={58, 45, 48, 89, 7412, 35};
int temp = 0;
for (int i = 0; i < array.length; i++){
boolean swapped = true; // 用于检查是否在一趟遍历中发生了交换的标志
for (int j = 0; j < array.length - 1; j++){
if (array[j] >= array[j+1]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
swapped = false; // 设置标志,表示发生了交换
}
}
if (swapped){
break; // 如果在一趟遍历中没有发生交换,说明数组已经有序,退出循环
}
System.out.println(Arrays.toString(array)); // 在每一趟遍历后打印数组
}
}
}
二分查找法
二分法检索(binary search)又称折半检索。
二分法检索的基本思想是设数组中的元素从小到大有序地存放在数组(array)中,首
先将给定值 key 与数组中间位置上元素的关键码(key)比较,如果相等,则检索成功;
否则,若 key 小,则在数组前半部分中继续进行二分法检索;
若 key 大,则在数组后半部分中继续进行二分法检索。
这样,经过一次比较就缩小一半的检索区间,如此进行下去,直到检索成功或检索失败。
【示例】二分法查找法的基本算法
class Solution {
public int search(int[] nums, int target) {
// 如果目标值小于数组的最小值或大于数组的最大值,直接返回-1,不需要进行二分查找
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
int left = 0; // 左指针,初始指向数组的起始位置
int right = nums.length - 1; // 右指针,初始指向数组的末尾位置
while (left <= right) { // 当左指针小于等于右指针时,进行循环
int mid = left + ((right - left) >> 1); // 计算中间位置,使用位移运算优化除以2的操作
if (nums[mid] == target) { // 如果中间位置的值等于目标值,找到目标值,返回中间位置
return mid;
}
else if (nums[mid] < target) { // 如果中间位置的值小于目标值,则目标值可能在右半部分,更新左指针为mid+1
left = mid + 1;
}
else if (nums[mid] > target) { // 如果中间位置的值大于目标值,则目标值可能在左半部分,更新右指针为mid-1
right = mid - 1;
}
}
return -1; // 没有找到目标值,返回-1
}
}