由于一些不便透露的原因,平时分查询功能暂时下线了。
本站将会推出一个新功能,实时成绩查询功能,即在新的考试成绩公布时通过邮件等形式进行通知,免去不断刷新成绩的烦恼。如果对该功能有什么建议,欢迎大家留言评论。
什么是 GitHub Actions
GitHub Actions
是GitHub提供的一个持续集成,持续部署工具。您可以直接在 GitHub 仓库中通过 GitHub Actions 创建自定义持续集成 (CI) 和持续部署 (CD) 工作流程。
如何使用 GitHub Actions
在git项目中开启Actions功能
打开GitHub项目的主页,找到这个按钮,点击,即可进入Actions页面。
选择一个合适的配置文件,将其加入你的项目中,即完成了持续集成的配置工作。
配置文件的样例
这是一个配置文件的样例。使用该配置文件,可以用于gradle构建的Spring Boot
项目。在项目进行更新时,自动生成Spring Boot
的jar
文件,并发布release
。
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Modify gradle config file
run: |
sed -e '/maven.aliyun.com/d' build.gradle >> build.gradle.1
sed -e '/maven.aliyun.com/d' settings.gradle >> settings.gradle.1
mv build.gradle.1 build.gradle
mv settings.gradle.1 settings.gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew bootjar
- name: Get Release Info
id: get_info
run: |
VERSION=`grep version build.gradle | grep '=' | grep -Eo \'.*\' | grep -Eo '[a-z|A-Z|0-9|.|_]*'`
NAME=`grep "rootProject.name" settings.gradle | grep -Eo \'.*\' | grep -Eo '[a-z|A-Z|0-9|.|_]*'`
FILE_PATH="./build/libs/"$NAME"-"$VERSION".jar"
FILE_NAME=$NAME"-"$VERSION".jar"
VERSION=$VERSION.`git rev-parse --short HEAD`
echo ::set-output name=file_path::$FILE_PATH
echo ::set-output name=file_name::$FILE_NAME
echo ::set-output name=version::$VERSION
- name: Create Release
id: create_release
uses: actions/create-release@master
env:
GITHUB_TOKEN: ${{secrets.TOKEN}}
with:
tag_name: Release_${{steps.get_info.outputs.version}}
release_name: Release of version ${{steps.get_info.outputs.version}}
draft: false # 是否是草稿
prerelease: false # 是否是预发布
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@master
env:
GITHUB_TOKEN: ${{secrets.TOKEN}}
with:
upload_url: ${{steps.create_release.outputs.upload_url}}
asset_path: ${{steps.get_info.outputs.file_path}}
asset_name: ${{steps.get_info.outputs.file_name}}
asset_content_type: application/java-archive
- name: rename file
run: |
mv ./build/libs/${{steps.get_info.outputs.file_name}} ./build/libs/${{steps.get_info.outputs.file_name}}.`git rev-parse --short HEAD`
- name: deploy file
uses: wlixcc/SFTP-Deploy-Action@v1.0
with:
username: 'server_runner'
server: 'huhaorui.com'
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
local_path: './build/libs/*'
remote_path: '/www/wwwroot/fridge.huhaorui.com'
args: '-o ConnectTimeout=5'
- name: restart server
run: |
mkdir ~/.ssh
ssh-keyscan huhaorui.com >> ~/.ssh/known_hosts
echo "${{ secrets.SSH_PRIVATE_KEY }}" >> ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh 'server_runner@huhaorui.com' "kill -9 \`ps -x | grep fridge_server | sed -n '1p' | grep -Eo [0-9]{4}[0-9]+\`"
ssh 'server_runner@huhaorui.com' "nohup java -jar /www/wwwroot/fridge.huhaorui.com/${{steps.get_info.outputs.file_name}}.`git rev-parse --short HEAD` >/dev/null 2>&1 &"
如何写一个配置文件
通过研究上面的配置,可以发现,我们可以在配置中使用已有的action,如actions/upload-release-asset@master
,也可以自己编写bash脚本,用于实现特定的功能。
更多使用方法可以参阅GitHub提供的文档。
什么是Stream API
关于stream,IBM对其有一个概括。链接
Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。
怎么使用stream API
获得一个stream对象
对于实现了List
接口的对象,获取stream非常简单。
List<Integer> nums = new ArrayList<>();
Stream<Integer> stream = nums.stream();
调用List
的stream()
方法,即可得到一个Stream
对象。
而对于int[]
一类的数组,获取stream也不是很困难。
int[] nums = {1, 2, 3, 4, 5, 6, 7};
IntStream stream = Arrays.stream(nums);
由于Java的基本数据类型导致的问题,此处的stream不再为Stream<T>
类型(毕竟int并不是对象),而是一个所谓的IntStream
,不过这并不影响其的使用。要获得Stream<Integer>
类型的stream,我们只需要将数组更换为Integer[]
类型。
Integer[] nums = {1, 2, 3, 4, 5, 6, 7};
Stream<Integer> stream = Arrays.stream(nums);
stream有什么用?
获得了一个stream对象之后,我们就可以开始利用它。
求数组长度
最简单的,调用stream.count()
方法,即可获得数组的长度。
Integer[] nums = {1, 2, 3, 4, 5, 6, 7};
Stream<Integer> stream = Arrays.stream(nums);
long size = Arrays.stream(nums).count();
//上下两行代码等价
size = nums.length;
有人可能会说,就这?这只是stream最简单的使用,接着来些好玩的例子。
统计不及格学生的学号
Map<String, Integer> score = new HashMap<>();
score.put("201806061100", 95);
score.put("201806061101", 75);
score.put("201806061102", 53);
score.put("201806061103", 54);
score.put("201806061104", 77);
score.keySet().stream()
.filter(key -> score.get(key) < 60)
.forEach(key -> System.out.println(key));
该例子使用了stream的filter方法,其中使用了一个lambda表达式,传入一个key,返回key所对应的value是否小于60。这个filter会返回所有成绩低于60分的学生的学号。
接下来,使用了forEach方法,继续利用lambda表达式,将这些学号依次输出。
将stream转回List
List<String> blame = score.keySet().stream()
.filter(key -> score.get(key) < 60)
.collect(Collectors.toList());
使用collect方法,指定转换方式为toList
,即可将其转回List<String>
对值进行额外的处理
score.keySet().stream()
.filter(key -> score.get(key) < 60)
.map(key -> key.substring(8))
.forEach(key->System.out.println(key));
在这里,我们使用了一个新方法 map
,可以将其解释为映射
。.map(key -> key.substring(8))
,会返回一个新的stream对象,其中的每一项都执行了 .substring(8)
的操作。
排序
Map<String, Integer> score = new HashMap<>();
score.put("201806061100", 95);
score.put("201806061101", 75);
score.put("201806061102", 53);
score.put("201806061103", 54);
score.put("201806061104", 77);
score.keySet().stream()
.sorted((k1, k2) -> score.get(k2) - score.get(k1))
.forEach(key -> System.out.println(key));
使用 sorted
方法,并在其中实现一个比较器,即可完成按照成绩从高到低的顺序进行排序。
更多有趣的方法
有关stream的方法还有很多,读者可自行进入Java的文档进行了解,本文不再进行介绍。
引言
一直以来,Java有一个为人所不满的缺点:啰嗦。有些简单的东西,可能需要撰写更多代码才能完成。
幸好,Java为我们推出了很多新功能,来解决其中的问题。本文主要想介绍Java8的lambda表达式。
lambda
之前的版本
先来看这么一串代码
String[] name = {"Mike", "Tom", "Jerry", "Potty", "Moggy"};
Arrays.sort(name, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
有五个小朋友,他们有不同的名字。我们希望按照字母的顺序为其排序。按照Java7的写法,我们需要完成一个Comparator
接口,并实现其中的compare
方法。以完成排序。
使用lambda的写法
使用IDEA的朋友们可能发现,之前的代码被标为了黄色。IDEA会将你的代码改成这样
Arrays.sort(name, (o1, o2) -> o1.compareTo(o2));
原本的compare
函数消失了,变成了 (o1, o2) -> o1.compareTo(o2)
。这是怎么回事呢?
什么是lambda
其实这就是lambda表达式,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。(o1, o2) -> o1.compareTo(o2)
意为,传入了o1,o2两个参数,将 o1.compareTo(o2)
的值作为返回值。简单的语法就完成了原本五六行代码的工作量。
还能更简单吗?
能!如果使用Java8的另外一个特性,方法引用
,撰写的代码量会更少。有关方法引用的使用,我会在之后的文章里进行介绍。
lambda的更多示例
启动子线程
new Thread(() -> System.out.println("Hello World")).start();
等价于
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
显而易见,使用lambda语句,可以使代码更为简洁。
使用滑动窗口算法解决一些问题
在Leetcode上做到了一道题。
题目
给你两个长度相同的字符串,s
和 t
。
将 s
中的第 i
个字符变到 t
中的第 i
个字符需要 |s[i] - t[i]|
的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。
用于变更字符串的最大预算是 maxCost
。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
如果你可以将 s
的子字符串转化为它在 t
中对应的子字符串,则返回可以转化的最大长度。
如果 s
中没有子字符串可以转化成 t
中对应的子字符串,则返回 0。
解答
删去其中繁杂的题干,题意大概是从一个自然数串中,找出最长的和小于 maxCost
的串。此题可以用滑动窗口的思路解决。
滑动窗口
定义一个左指针 leftPtr
,一个右指针 rightPtr
,开始时将两个指针都置于最左边。右侧指针每次向右移动一格,判断左指针所处的位置是否符合题意。如果不符合,将其向右移动,直到符合题意为止。存储最大的符合题意的距离,即为答案。
简单说来,可以理解成右指针拉着左指针向右动,它们之间用一根线(maxCost
)相连。如果线足够长(rightPtr - leftPtr <= maxCost
),左指针就不会动,否则它也会跟着向右移。
代码
class Solution {
public int equalSubstring(String s, String t, int maxCost) {
int length = s.length();
int[] cost = new int[length];
for (int i = 0; i < length; i++) {
cost[i] = Math.abs(s.charAt(i) - t.charAt(i));
}
int rightPtr = 0, leftPtr = 0, max = 0, nowCost = 0;
while (rightPtr < length) {
nowCost += cost[rightPtr];
while (nowCost > maxCost) {
nowCost -= cost[leftPtr];
leftPtr++;
}
if (rightPtr - leftPtr + 1 > max) {
max = rightPtr - leftPtr + 1;
}
rightPtr++;
}
return max;
}
}