Java常见算法和Lambda表达式
常见算法
查找算法
基本查找
package search;
public class BasicSearchDemo1 {
public static void main(String[] args) {
// 基本查找 / 顺序查找
// 核心:
// 从 0 索引开始挨个往后查找
// 需求:定义一个方法利用基本查找,查询某个元素是否存在
// 数据如下:{131, 127, 147, 81, 103, 23, 7, 79}
int[] arr = {131, 127, 147, 81, 103, 23, 7, 79};
boolean result = basicSearch(arr, 23);
System.out.println(result);
}
public static boolean basicSearch(int[] arr, int target){
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return true;
}
}
return false;
}
}
package search;
import java.util.ArrayList;
public class BasicSearchDemo2 {
public static void main(String[] args) {
// 练习1:
// 需求:定义一个方法利用基本查找,查询某个元素在数组中的索引
// 要求:不需要考虑数组中元素是否重复
int[] arr1 = {131, 127, 147, 81, 103, 23, 7, 79};
int index1 = basicSearchIndex1(arr1, 131);
System.out.println(index1);
// 练习2:
// 需求:定义一个方法利用基本查找,查询某个元素在数组中的索引
// 要求:需要考虑数组中元素有重复的可能性
int[] arr2 = {131, 127, 127, 81, 7, 127, 103, 23, 7, 79};
ArrayList<Integer> index2 = basicSearchIndex2(arr2, 127);
ArrayList<Integer> index3 = basicSearchIndex2(arr2, 7);
System.out.println(index2);
System.out.println(index3);
}
// 练习1:
// 需求:定义一个方法利用基本查找,查询某个元素在数组中的索引
// 要求:不需要考虑数组中元素是否重复
public static int basicSearchIndex1(int[] arr, int target){
for (int i = 0; i < arr.length; i++) {
if(arr[i] == target){
return i;
}
}
return -1;
}
// 练习2:
// 需求:定义一个方法利用基本查找,查询某个元素在数组中的索引
// 要求:需要考虑数组中元素有重复的可能性
public static ArrayList<Integer> basicSearchIndex2(int[] arr, int target){
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
if(arr[i] == target){
list.add(i);
}
}
return list;
}
}
二分查找 / 折半查找
前提条件:数组中的数据必须是有序的 核心思想:每次排除一半的查找范围
package search;
public class BinarySearchDemo1 {
public static void main(String[] args) {
// 二分查找 / 折半查找
// 核心:
// 每次排除一半的查找范围
// 需求:定义一个方法利用二分查找,查询某个元素在数组中的索引
// 数据如下:{7, 23, 79, 81, 103, 127, 131, 147}
int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};
System.out.println(binarySearch(arr, 103));
}
// 二分查找 / 折半查找
// 核心:
// 每次排除一半的查找范围
// 需求:定义一个方法利用二分查找,查询某个元素在数组中的索引
// 数据如下:{7, 23, 79, 81, 103, 127, 131, 147}
public static int binarySearch(int[] arr, int target){
int len = arr.length;
int min = 0;
int max = len - 1;
while(min <= max){
int mid = (max + min) / 2;
if(arr[mid] > target){
max = mid - 1;
}else if(arr[mid] == target){
return mid;
}else{
min = mid + 1;
}
}
return -1;
}
}
总结
- 二分查找的优势?
提前查找效率
- 二分查找的前提条件?
数据必须是有序的
如果数据是乱的,先排序再用二分查找得到的索引没有实际意义,只能确定当前数字在数组中是否存在, 因为排序之后数字的位置可能发生变化了
- 二分查找的过程
- min 和 max 表示当前要查找的范围
- mid 是在 min 和 max 中间的
- 如果要查找的元素在 mid 的左边,缩小范围时,min 不变,max 等于 mid 减 1
- 如果要查找的元素在 mid 的右边,缩小范围时,max 不变,min 等于 mid 加 1
二分查找改进
插值查找

适用于接近等差数列的数据。
斐波那契查找
透过黄金分割率1 : 0.618查找斐波那契数列中的值,其中:mid = min + 黄金分割点左半边长度 - 1
分块查找
分块的原则1:前一块中的最大数据,小于后一块中所有的数据(块内无序,块间有序)
分块的原则2:块数数量一般等于数字的个数开根号。比如:16个数字一般分为4块左右。
核心思路:先确定要查找的元素在哪一块,然后在块内挨个查找。
package search;
public class BlockSearchDemo {
public static void main(String[] args) {
/*
分块查找
核心思想:
块内无序,块间有序
实现步骤:
1. 创建数组 blockArr 存放每一个块对象的信息
2. 先查找 blockArr 确定要查找的数据属于哪一块
3. 再单独遍历这一块数据即可
*/
int[] arr = {16, 5, 9, 12, 21, 18,
32, 23, 37, 26, 45, 34,
50, 48, 61, 52, 73, 66};
// 1. 要把数据进行分块
// 要分为几块:arr.length 开根 -- 4.24
// 18 / 4 = 4.5 个
Block b1 = new Block(21, 0, 5);
Block b2 = new Block(37, 6, 11);
Block b3 = new Block(73, 12, 17);
// 定义数组用来管理三个块的对象(索引表)
Block[] blockArr = {b1, b2, b3};
// 定义一个变量用来记录查找的元素
int target = 30;
// 调用方法,传递索引表,数组,要查找的元素
int index = getIndex(blockArr, arr, target);
// 打印一下
System.out.println(index);
}
// 利用分块查找的原理。查询 target 的索引
private static int getIndex(Block[] blockArr, int[] arr, int target) {
// 1. 确定 target 是在哪一块当中
int indexBlock = findIndexBlock(blockArr, target);
if(indexBlock == -1){
// 表示 target 不在数组当中
return -1;
}
// 获取这一块的起始索引和结束索引
int startIndex = blockArr[indexBlock].getStartIndex();
int endIndex = blockArr[indexBlock].getEndIndex();
for (int i = startIndex; i <= endIndex; i++) {
if(arr[i] == target){
return i;
}
}
return -1;
}
// 定义一个方法,用来确定 target 在哪一块当中
public static int findIndexBlock(Block[] blockArr, int target){// 30
// Block b1 = new Block(21, 0, 5); --- 0
// Block b2 = new Block(37, 6, 11); --- 1
// Block b3 = new Block(73, 12, 17); --- 2
// 从 0 索引开始遍历 blockArr,如果 target 小于 max,那么就表示 target 是在这一块当中的
for (int i = 0; i < blockArr.length; i++) {
if(target <= blockArr[i].getMax()){
return i;
}
}
return -1;
}
}
class Block{
private int max;// 最大值
private int startIndex;// 起始索引
private int endIndex;// 结束索引
public Block() {
}
public Block(int max, int startIndex, int endIndex) {
this.max = max;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
/**
* 获取
* @return max
*/
public int getMax() {
return max;
}
/**
* 设置
* @param max
*/
public void setMax(int max) {
this.max = max;
}
/**
* 获取
* @return startIndex
*/
public int getStartIndex() {
return startIndex;
}
/**
* 设置
* @param startIndex
*/
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
/**
* 获取
* @return endIndex
*/
public int getEndIndex() {
return endIndex;
}
/**
* 设置
* @param endIndex
*/
public void setEndIndex(int endIndex) {
this.endIndex = endIndex;
}
public String toString() {
return "Block{max = " + max + ", startIndex = " + startIndex + ", endIndex = " + endIndex + "}";
}
}
分开查找(无规律的数据)
package search;
public class BlockSearchDemo2 {
public static void main(String[] args) {
int[] arr = {27, 22, 30, 40, 36,
13, 19, 16, 20,
7, 10,
43, 50, 48};
Block2 b1 = new Block2(40, 0, 4);
Block2 b2 = new Block2(20, 5, 8);
Block2 b3 = new Block2(10, 9, 10);
Block2 b4 = new Block2(50, 11, 13);
Block2[] blockArr = {b1, b2, b3, b4};
sortBlockArr(blockArr);
int index = getIndex(blockArr, arr, 20);
System.out.println(index);
}
public static int getIndex(Block2[] blockArr, int[] arr, int target){
int indexBlock = findIndexBlock(blockArr, target);
if(indexBlock == -1){
return -1;
}
int startIndex = blockArr[indexBlock].getStartIndex();
int endIndex = blockArr[indexBlock].getEndIndex();
for (int i = startIndex; i <= endIndex; i++) {
if(arr[i] == target){
return i;
}
}
return -1;
}
public static int findIndexBlock(Block2[] blockArr, int target){
for (int i = 0; i < blockArr.length; i++) {
if(target <= blockArr[i].getMax()){
return i;
}
}
return -1;
}
// 冒泡排序:将块数组按最大值从小到大重新排序
public static void sortBlockArr(Block2[] blockArr){
for (int i = 0; i < blockArr.length - 1; i++) {
for (int j = 0; j < blockArr.length - 1 - i; j++) {
if(blockArr[j].getMax() > blockArr[j + 1].getMax()){
Block2 temp = blockArr[j];
blockArr[j] = blockArr[j + 1];
blockArr[j + 1] = temp;
}
}
}
}
}
class Block2{
private int max;
private int startIndex;
private int endIndex;
public Block2() {
}
public Block2(int max, int startIndex, int endIndex) {
this.max = max;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
/**
* 获取
* @return max
*/
public int getMax() {
return max;
}
/**
* 设置
* @param max
*/
public void setMax(int max) {
this.max = max;
}
/**
* 获取
* @return startIndex
*/
public int getStartIndex() {
return startIndex;
}
/**
* 设置
* @param startIndex
*/
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
/**
* 获取
* @return endIndex
*/
public int getEndIndex() {
return endIndex;
}
/**
* 设置
* @param endIndex
*/
public void setEndIndex(int endIndex) {
this.endIndex = endIndex;
}
public String toString() {
return "Block2{max = " + max + ", startIndex = " + startIndex + ", endIndex = " + endIndex + "}";
}
}
总结
总共有七种查找方式:基本查找,二分查找,插值查找,斐波那契查找,分块查找,哈希查找,树表查找。
排序算法
冒泡排序
冒泡排序:相邻的数据两两比较,小的放前面,大的放后面。
package com.faxont.sort;
public class BubbleDemo1 {
public static void main(String[] args) {
/*
冒泡排序:
核心思想:
1. 相邻的元素两两比较,大的放右边,小的放左边
2. 第一轮比较完毕之后,最大值就已经确定,第二轮可以少循环一次,后面以此类推
3. 如果数组中有n个数据,总共我们只需要执行n-1轮的代码就可以
*/
// 1. 定义数组
int[] arr = {2, 4, 5, 3, 1};
// 2. 创建方法,完成排序
bubbleSort(arr);
printArray(arr);
}
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
public static void bubbleSort(int[] arr){
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
选择排序
选择排序:从0索引开始,拿着每一个索引上的元素跟后面的元素依次比较,小的放前面,大的放后面,以此类推。
package com.faxont.sort;
public class SelectionDemo {
public static void main(String[] args) {
/*
选择排序:
1. 从 0 索引开始,跟后面的元素一一比较
2. 小的放前面,大的放后面
3. 第一次循环结束后,最小的数据已经确定
4. 第二次循环从 1 索引开始以此类推
*/
// 1. 定义数组
int[] arr = {2, 4, 5, 3, 1};
selectionSort(arr);
printArray(arr);
}
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
public static void selectionSort(int[] arr){
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if(arr[j] < arr[minIndex]){
minIndex = j;
}
}
if(minIndex != i){
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
}
插入排序
插入排序:将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的,遍历无序的数据,将遍历的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。(N的范围:0~最大索引)
package com.faxont.sort;
public class InsertDemo {
public static void main(String[] args) {
/*
插入排序:
将 0 索引的元素到N索引的元素看做是有序的,把 N + 1 索引的元素到最后一个当成是无序的,
遍历无序的数据,将遍历的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。
(N的范围:0 ~ 最大索引)
*/
int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
insertSort(arr);
printArray(arr);
}
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
public static void insertSort(int[] arr){
int startIndex = -1;
for (int i = 0; i < arr.length - 1; i++) {
if(arr[i] > arr[i + 1]){
startIndex = i + 1;
break;
}
}
for (int i = startIndex; i < arr.length; i++) {
int j = i;
while(j > 0 && arr[j] < arr[j - 1]){
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
j--;
}
}
}
}
递归算法
递归指的是方法中调用算法本身的现象。
递归的注意点:递归一定要有出口,否则就会出现内存溢出。
递归算法的作用
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算。
书写递归的两个核心:
- 找出口:什么时候不再调用方法。
- 找规则:如何把大问题变成规模较小的问题
递归求和
需求:求1-100之间的和
package com.faxont.sort;
public class RecursionDemo1 {
public static void main(String[] args) {
// 需求:利用递归求1-100之间的和
// 100 + 99 + 98 + ... + 1
System.out.println(getSum(100));
}
public static int getSum(int n){
if(n == 1){
return 1;// 递归的出口
}
return n + getSum(n - 1);
}
}
递归求阶乘
需求:用递归求5的阶乘,并把结果在控制台输出
快速排序
package com.faxont.sort;
public class QuickSortDemo {
public static void main(String[] args) {
/*
快速排序:
第一轮:以 0 索引的数字为基准数,确定基准数在数字中正确的位置
比基准数小的全部在左边,比基准数大的全部在右边
后面以此类推
*/
int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
quickSort(arr, 0, arr.length - 1);
printArray(arr);
}
private static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
/**
*
* @param arr
* @param i
* @param j
*/
public static void quickSort(int[] arr, int i, int j){
// 记录基准数
int start = i;
int end = j;
// 必须在基准数前,否则会越界
if(start > end){
// 递归的出口
return;
}
int base = arr[i];
// 利用循环找到要交换的数字
while(start != end){
// 利用 j,从后往前开始找,找比基准数小的数字
while(true){
if(end <= start || arr[end] < base){
break;
}
end--;
}
// 利用 i,从前往后开始找,找比基准数大的数字
while(true){
if(end <= start || arr[start] > base){
break;
}
start++;
}
// 把 j 的数字和 i 的数字交换
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
// 当 i 和 j 指向了同一个元素的时候,那么上面的循环就会结束
// 表示已经找到了基准数在数组中应存入的位置
// 基准数归位
// 就是拿着这个范围中的第一个数字,则 start 指向的元素进行交换
int temp = arr[i];
arr[i] = arr[start];
arr[start] = temp;
// 确定基准数左边的范围
quickSort(arr, i, start - 1);
// 确定基准数右边的范围
quickSort(arr, start + 1, j);
}
}
Arrays
操作数组的工具类。
| 方法名 | 说明 |
|---|---|
| public static String toString(数组) | 把数组拼成一个字符串 |
| public static int binarySearch(数组, 查找的元素) | 二分查找法查找元素 |
| public static int[] copyOf(原数据, 新数组长度) | 拷贝数组 |
| public static int[] copyOfRange(原数组, 起始索引, 结束索引) | 拷贝数组(指定范围) |
| public static void fill(数组, 元素) | 填充数组 |
| public static void sort(数组) | 按照默认方式进行数组排序 |
| public static void sort(数组, 排序规则) | 按照指定的规则排序 |
package com.faxont.arrays;
import java.util.Arrays;
public class ArraysDemo1 {
public static void main(String[] args) {
/*
public static String toString(数组) 把数组拼成一个字符串
public static int binarySearch(数组, 查找的元素) 二分查找法查找元素
public static int[] copyOf(原数据, 新数组长度) 拷贝数组
public static int[] copyOfRange(原数组, 起始索引, 结束索引) 拷贝数组(指定范围)
public static void fill(数组, 元素) 填充数组
public static void sort(数组) 按照默认方式进行数组排序
*/
// toString:将数组变成字符串
System.out.println("--------------------toString--------------------");
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println(Arrays.toString(arr));
// binarySearch:二分查找法查找元素(同时要求数组中的元素是升序的)
// 细节1:二分查找的前提:数组中的元素必须是有序的
// 细节2:如果要查找的元素是存在的,那么返回的是真实的索引
// 但是,如果要查找的元素是不存在的,返回的是 - 插入点 - 1
// 疑问:为什么还要再减一呢?
// 解释:如果此时,我现在要查找数字 0,那么如果返回的是 - 插入点,就会出现问题了
// 为了避免这样的情况,Java 在这个基础上又减了一
System.out.println("--------------------binarySearch--------------------");
System.out.println(Arrays.binarySearch(arr, 10));
System.out.println(Arrays.binarySearch(arr, 2));
System.out.println(Arrays.binarySearch(arr, 20));
// copyOf:拷贝数组
// 参数一:原数组
// 参数二:新数组的长度
// 方法的底层会根据第二个参数来创建新的数组
// 如果新数组的长度小于原数组的长度,会部分拷贝
// 如果新数组的长度等于原数组的长度,会全部拷贝
// 如果新数组的长度大于原数组的长度,会全部拷贝并在其余部分补上默认初始值
System.out.println("--------------------copyOf--------------------");
int[] newArr1 = Arrays.copyOf(arr, 20);
System.out.println(Arrays.toString(newArr1));
// copyOfRange:拷贝数组(指定范围)
// 细节:包头包尾,包左不包右
System.out.println("--------------------copyOfRange--------------------");
int[] newArr2 = Arrays.copyOfRange(arr, 0, 9);
System.out.println(Arrays.toString(newArr2));
// fill:填充数组
System.out.println("--------------------fill--------------------");
Arrays.fill(arr, 100);
System.out.println(Arrays.toString(arr));
// sort:排序,默认情况下,给基本数据类型进行升序排列
System.out.println("--------------------sort--------------------");
int[] arr2 = {10, 2, 3, 5, 6, 1, 7, 8, 4, 9};
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
}
}
package com.faxont.arrays;
import java.util.Arrays;
import java.util.Comparator;
public class ArraysDemo2 {
public static void main(String[] args) {
/*
public static void sort(数组, 排序规则) 按照指定的规则排序
参数一:要排序的数组
参数二:排序的规则
细节:
只能给引用数据类型的数组进行排序
如果数组是基本数据类型的,需要变成其对应的包装类
*/
Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9};
// 第二个参数是一个接口,所以我们在调用方法的时候,需要传递这个接口的实现类对象,作为排序的规则
// 但是这个实现类,我只要使用一次,所以就没有必要单独地去写一个类,直接采取匿名内部类的方式就可以了
// 底层原理:
// 利用插入排序 + 二分查找的方式进行排序的
// 默认把 0 索引的数据当作是有序的序列,1 索引到最后认为是无序的序列
// 遍历无序的序列得到里面的每一个元素,假设当前遍历得到的元素是 A 元素
// 把 A 往有序序列中进行输入,在输入的时候,是利用二分查找确定 A 元素的插入点的
// 拿着 A 元素,跟插入点的元素进行比较,比较的规则就是 compare 方法的方法体
// 如果方法的返回值是负数,拿着 A 继续跟前面的数据进行比较
// 如果方法的返回值是正数,拿着 A 继续跟后面的数据进行比较
// 如果方法的返回值是0,也拿着 A 继续跟后面的数据进行比较
// 直到能确定 A 的最终位置为止
// compare 方法的形式参数:
// 参数一 o1:表示在无序序列中,遍历得到的每一个元素
// 参数二 o2:表示有序序列中的元素
// 返回值:
// 负数:表示当前要插入的元素是小的,放在前面
// 正数:表示当前要插入的元素是大的,放在后面
// 负数:表示当前要插入的元素跟现在的元素比是一样的,也放在后面
// 简单理解:
// o1 - o2:升序排列
// o2 - o1:降序排列
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("-----------------");
System.out.println("o1 : " + o1);
System.out.println("o2 : " + o2);
return o1 - o2;
}
});
System.out.println(Arrays.toString(arr));
}
}
Lambda
函数式编程
函数式编程(Functional programming)是一种思想特点。
函数式编程思想,忽略面向对象的复杂语法,强调做什么,而不是谁去做。
而我们要学习的 Lambda 表达式就是函数式思想的体现。
Lambda 表达式的标准格式
Lambda 表达式是 JDK 8 开始后的一种新语法形式。
() -> {
}
- () 对应着方法的形参
- -> 固定格式
- {} 对应着方法的方法体
package com.faxont.lambda;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaDemo1 {
public static void main(String[] args) {
// 初识 lambda 表达式
Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9};
// Arrays.sort(arr, new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// return o1 - o2;
// }
// });
Arrays.sort(arr, (Integer o1, Integer o2) -> {
return o1 - o2;
});
System.out.println(Arrays.toString(arr));
}
}
注意点:
- Lambda 表达式可以用来简化匿名内部类的书写
- Lambda 表达式只能简化函数式接口的匿名内部类的写法
- 函数式接口:
有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加 @FuncationalInterface 注解
package com.faxont.lambda;
public class LambdaDemo2 {
public static void main(String[] args) {
/*
Lambda 表达式的注意点:
1. Lambda 表达式可以用来简化匿名内部类的书写
2. Lambda 表达式只能简化函数式接口的匿名内部类的写法
3. 函数式接口:
有且仅有一个抽象方法的接口叫做函数式接口,接口上方可以加 FunctionalInterface 注解
*/
// 1. 利用匿名内部类的形式去调用下面的方法
// 调用一个方法的时候,如果方法的形参是一个接口,那么我们要传递这个接口的实现类对象
// 如果实现类对象只要用到一次,就可以用匿名内部类的形式进行书写
method(new Swim(){
@Override
public void swimming() {
System.out.println("正在游泳...");
}
});
// 2. 利用 Lambda 表达式进行改写
method(
() -> {
System.out.println("正在游泳...");
}
);
}
public static void method(Swim s){
s.swimming();
}
}
interface Swim{
public abstract void swimming();
}
总结
-
Lambda 表达式的基本作用
简化函数式接口的匿名内部类的写法。
-
Lambda 表达式有什么使用前提
必须是接口的匿名内部类,接口中只能有一个抽象方法。
-
Lambda 表达式的好处
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码,它可以写出更简洁、灵活的代码,作为一个更紧凑的代码风格,使 Java 语言表达能力得到了提升。
Lambda 表达式的省略写法
省略核心:可推导,可省略
- 参数类型可以省略不写
- 如果只有一个参数,参数类型可以省略,同时()也可以省略
- 如果 Lambda 表达式的方法体只有一行,大括号,分号,return 可以省略不写,需要同时省略
package com.faxont.lambda;
import java.util.Arrays;
import java.util.Comparator;
public class LambdaDemo3 {
public static void main(String[] args) {
/*
lambda 的省略规则:
1. 参数类型可以省略不写
2. 如果只有一个参数,参数类型可以省略,同时()也可以省略
3. 如果 Lambda 表达式的方法体只有一行,大括号,分号,return 可以省略不写,需要同时省略
*/
Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9};
// Arrays.sort(arr, new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// return o1 - o2;
// }
// });
//
// // Lambda 表达式完整格式
// Arrays.sort(arr, (Integer o1, Integer o2) -> {
// return o1 - o2;
// });
// Lambda 表达式省略格式
Arrays.sort(arr, (o1, o2) -> o1 - o2);
System.out.println(Arrays.toString(arr));
}
}
练习:Lambda 表达式简化 Comparator 接口的匿名形式
定义数组并存储一些字符串,利用 Arrays 中的 sort 方法进行排序
要求:
按照字符串的长度进行排序,短的在前面,长的在后面。
(暂时不比较字符串里面的内容)
package com.faxont.lambda;
import java.util.Arrays;
public class LambdaDemo4 {
public static void main(String[] args) {
/*
定义数组并存储一些字符串,利用 Arrays 中的 sort 方法进行排序
要求:
按照字符串的长度进行排序,短的在前面,长的在后面。
(暂时不比较字符串里面的内容)
*/
String[] arr = {"Faxont", "Dead by Dying", "Strange Things", "Doom", "Tomb Raider"};
Arrays.sort(arr, (o1, o2) -> o1.length() - o2.length());
System.out.println(Arrays.toString(arr));
}
}
综合练习
练习:按照要求进行排序
定义数组并存储一些对象,利用 Arrays 中的 sort 方法进行排序
要求1:属性有姓名、年龄、身高
要求2:按照年龄的大小进行排序,年龄一样,按照身高排序,身高一样按照姓名的字母进行排序(姓名中不要有中文或特殊字符,会涉及到后面的知识)
package com.faxont.practice;
import java.util.Arrays;
import java.util.Comparator;
public class Practice1 {
public static void main(String[] args) {
/*
定义数组并存储一些对象,利用 Arrays 中的 sort 方法进行排序
要求1:属性有姓名、年龄、身高
要求2:按照年龄的大小进行排序,年龄一样,按照身高排序,身高一样按照姓名的字母进行排序(姓名中不要有中文或特殊字符,会涉及到后面的知识)
*/
// 1. 创建对象
Person p1 = new Person("zhangsan", 18, 180);
Person p2 = new Person("lisi", 17, 170);
Person p3 = new Person("wangwu", 18, 170);
// 2. 创建数组
Person[] arr = {p1, p2, p3};
// 3. 利用 Arrays 中的 sort 方法进行排序
// 匿名内部类
// Arrays.sort(arr, new Comparator<Person>() {
// @Override
// public int compare(Person o1, Person o2) {
// double temp = o1.getAge() - o2.getAge();
// temp = temp == 0 ? temp = o1.getHeight() - o2.getHeight() : temp;
// temp = temp == 0 ? temp = o1.getName().charAt(0) - o2.getName().charAt(0) : temp;
// if(temp > 0){
// return 1;
// }else if(temp < 0){
// return -1;
// }else{
// return 0;
// }
// }
// });
// Lambda 表达式
Arrays.sort(arr, (o1, o2) -> {
double temp = o1.getAge() - o2.getAge();
temp = temp == 0 ? temp = o1.getHeight() - o2.getHeight() : temp;
temp = temp == 0 ? temp = o1.getName().charAt(0) - o2.getName().charAt(0) : temp;
if(temp > 0){
return 1;
}else if(temp < 0){
return -1;
}else{
return 0;
}
});
// 4. 输出
System.out.println(Arrays.toString(arr));
}
}
package com.faxont.practice;
public class Person {
private String name;
private int age;
private double height;
public Person() {
}
public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return height
*/
public double getHeight() {
return height;
}
/**
* 设置
* @param height
*/
public void setHeight(double height) {
this.height = height;
}
public String toString() {
return "Person{name = " + name + ", age = " + age + ", height = " + height + "}";
}
}
练习:不死神兔
有一个很有名的数学逻辑题叫做不死神兔问题,有一对兔子,从出生后第三个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第十二个月的兔子对数为多少?
package com.faxont.practice;
public class Practice2 {
public static void main(String[] args) {
/*
有一个很有名的数学逻辑题叫做不死神兔问题,有一对兔子,从出生后第三个月起每个月都生一对兔子,
小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第十二个月的兔子对数为多少?
*/
System.out.println(rabbitRecursion(12));
System.out.println(rabbitDP(12));
}
// 递归 时间复杂度为O(2^n)
public static int rabbitRecursion(int month){
if(month <= 2){
return 1;
}else{
return rabbitRecursion(month - 1) + rabbitRecursion(month - 2);
}
}
// 动态规划 时间复杂度为O(n)
public static int rabbitDP(int month){
int[] dp = new int[month];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i < dp.length; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[month - 1];
}
}
练习:猴子吃桃
有一堆桃子,猴子第一天吃了其中的一半,并多吃了一个。以后每天猴子都吃剩下来的一半,然后再多吃一个,第10天的时候(还没吃),发现只剩一个桃子了,请问,最初总共有多少个桃子?
package com.faxont.practice;
public class Practice3 {
public static void main(String[] args) {
/*
有一堆桃子,猴子第一天吃了其中的一半,并多吃了一个。
以后每天猴子都吃剩下来的一半,然后再多吃一个,
第10天的时候(还没吃),发现只剩一个桃子了,请问,最初总共有多少个桃子?
10-1 9-4 8-10 7-22 6-
1 4 10 22 46 94 190 334
*/
System.out.println(peach(10, 1));
}
public static int peach(int day, int count){
if(day == 1){
return count;
}
return peach(day - 1, (count + 1) * 2);
}
}
练习:爬楼梯
可爱的小明特别喜欢爬楼梯,他有的时候一次爬一个台阶,有的时候一次爬两个台阶。如果这个楼梯有20个台阶,小明一共有多少种爬法呢?
运算结果:
1层台阶 1种爬法
2层台阶 2种爬法
7层台阶 21种爬法
package com.faxont.practice;
public class Practice4 {
public static void main(String[] args) {
/*
可爱的小明特别喜欢爬楼梯,他有的时候一次爬一个台阶,有的时候一次爬两个台阶。如果这个楼梯有20个台阶,小明一共有多少种爬法呢?
运算结果:
1层台阶 1种爬法
2层台阶 2种爬法
3层台阶 3种爬法
4层台阶 5种爬法
5层台阶 8种爬法
6层台阶 13种爬法
7层台阶 21种爬法
*/
System.out.println(waysRecursion(20));
System.out.println(waysDP(20));
// 拓展:假设小明可以一步爬3个台阶,又有几种爬法呢
System.out.println(waysDP2(20));
}
// 递归
public static int waysRecursion(int n){
if(n == 1){
return 1;
}else if(n == 2){
return 2;
}else{
return waysRecursion(n - 1) + waysRecursion(n - 2);
}
}
// 动态规划
public static int waysDP(int n){
int[] dp = new int[n];
dp[0] = 1;
dp[1] = 2;
for (int i = 2; i < n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n - 1];
}
// 拓展:假设小明可以一步爬3个台阶,又有几种爬法呢
public static int waysDP2(int n){
int[] dp = new int[n];
dp[0] = 1;
dp[1] = 2;
dp[2] = 4;
for (int i = 3; i < n; i++) {
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
}
return dp[n - 1];
}
}

浙公网安备 33010602011771号