哺乳类的博客


  • 首页

  • 标签

  • 归档

ARTS打卡:第十五周

发表于 2019-07-06 | 分类于 ARTS

ARTS打卡:第十五周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

387. First Unique Character in a String(Easy)

Given a string, find the first non-repeating character in it and return it’s index. If it doesn’t exist, return -1.

Examples:

s = “leetcode”
return 0.

s = “loveleetcode”,
return 2.

Note: You may assume the string contain only lowercase letters.

@DevDocsleetcode.com/problems/first-unique-character-in-a-string

题意

给一串字符串,在字符串中找到第一个不重复的字符并返回该字符的下标,若果不存在,则返回 -1。

注意:你可以假设该字符串只包含小写字母。

解题思路(1)

这道题解法其实很简单,可以用暴力破解来做,双重遍历就能找出答案,不过我们作为新时代的好青年,一定是要有追求的😏,我们可以借助散列表,将时间复杂度从 O(n²) 降至 O(n)。
我就不多解释,直接上代码了😝。

代码实现(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public int firstUniqChar(String s) {
// 边界条件
if (s == null) {
return -1;
}
// 创建一个哈希 map
Map<Character, Integer> map = new HashMap<>(16);
int n = s.length();
// 首先遍历字符串中每个字符
for (int i = 0; i < n; i++) {
Character key = s.charAt(i);
// 将字符作为 key,在字符串中出现的次数作为 value;判断 map 中有无包含当前字符,有则 value + 1,无则,将当前字符作为 key,value 设为 1
map.put(key, map.getOrDefault(key, 0) + 1);
}

// 第二次遍历字符串,找出 map 中 value == 1 的字符,并返回当前下标
for (int i = 0; i < n; i++) {
Character key = s.charAt(i);
if (map.get(key) == 1) {
return i;
}
}
// 无符合要求的字符,返回 -1
return -1;
}
}

运行结果和复杂度分析(1)

结果:Runtime: 38 ms, faster than 38.79% of Java online submissions for First Unique Character in a String.
Memory Usage: 38.1 MB, less than 98.75% of Java online submissions for First Unique Character in a String.

时间复杂度:在最坏的情况下,即不存在无重复的字符,时间复杂度是O(2n),最好的情况下是 O(n),所以时间复杂度位是 O(n)。
空间复杂度:O(n)。

解题思路(2)

解法来源于别人提交记录,题目中有说明,我们可以假设输入的字符串是一串小写字母串,那么就可以从这个角度出发,借助 Java API 来实现,详细思路看代码注释。

代码实现(2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public int firstUniqChar(String s) {
// 边界条件
if (s == null) {
return -1;
}
// 字符串长度
int index = s.length();
// 字符串是小写字母组成的字符串,所以字符范围在 a ~ z
for (char i = 'a'; i <= 'z'; i++) {
// String.indexOf(返回第一次出现指定字符的字符串中的索引,没有出现则返回 -1),所以 s.indexOf(i) != -1
// String.lastIndexOf(返回指定字符的最后一次出现的字符串中的索引),所以 s.indexOf(i) == s.lastIndexOf(i) 时,该字符没有重复
// s.indexOf(i) < index 是要求出第一个没有重复字符
if (s.indexOf(i) != -1 && s.indexOf(i) == s.lastIndexOf(i) && s.indexOf(i) < index) {
index = s.indexOf(i);
}
}
// 当index != s.length() 证明存在没有重复的字符
if (index != s.length()) {
return index;
}
return -1;
}

运行结果和复杂度分析(2)

Runtime: 1 ms, faster than 100.00% of Java online submissions for First Unique Character in a String.
Memory Usage: 36.6 MB, less than 99.82% of Java online submissions for First Unique Character in a String.

时间复杂度:O(1)
空间复杂度:O(1)

Review

本周阅读:《The Joel Test: 12 Steps to Better Code》
这一篇很老的文章,发行于2000年,不过作者在文章中写到的内容,现在来看还是挺有价值的,值的小伙伴们阅读。

Tip

本周分享从极客时间的《SQL必知必会》专栏中学习到的技巧:

  • mysql> select @@profiling;:查看profiling是否开启,开启能让MySQL收集在执行SQL时所使用的资源情况
    • profiling = 1 表示开启
    • profiling = 0 表示关闭
      image
  • show profilings:查看当前会话产生的所有profiling
  • show profiling:获取上一次查询的执行时间
    image

Share

本周分享:

  • 梁大的文章:《颠覆微服务认知:深入思考微服务的七个主流观点》
  • 琦总的文章:《简单的 HTTP 调用,为什么时延这么大?》

ARTS打卡:第十四周

发表于 2019-07-03 | 分类于 ARTS

ARTS打卡:第十四周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

7. Reverse Integer(Easy)

Given a 32-bit signed integer, reverse digits of an integer.

Example 1:

Input: 123
Output: 321

Example 2:

Input: -123
Output: -321
Example 3:

Input: 120
Output: 21

Note:
Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−2^31, 2^31 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.

@DevDocsleetcode.com/problems/reverse-integer

大致题意

给定一个32位的带符号的整数,求翻转之后的整数。

代码实现

解题思路在代码注释,看注释就行了,这题虽然难度是 easy ,但我也是看讨论区才恍然大悟的,人与人之间的差距,一下子就显现出来了(:зゝ∠)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int reverse(int x) {
int result = 0;
while (x != 0) {
//求整数的尾部数字,比如 123,那么当前的尾数是 3
int tail = x % 10;
// 逐位数字进行翻转,比如 123,先翻转一位是 3,继续翻转则是 3*10 + 2,以此类推
int newResult = result * 10 + tail;
// 如果 result * 10 + tail 发生溢出,溢出值 newResult/10 是不等于 result 的,这里不减去尾部值 tail,是因为值为整形,除于10后,小数点会被移除。
if (newResult / 10 != result) {
return 0;
}
// 没有溢出则继续遍历
x /= 10;
result = newResult;
}
return result;
}

运行结果&复杂度分析

运行结果:
Runtime: 1 ms, faster than 100.00% of Java online submissions for Reverse Integer.
Memory Usage: 33.6 MB, less than 9.08% of Java online submissions for Reverse Integer.

时间复杂度:O(logx)
空间复杂度:O(1)

Review

本周阅读:《How to write code you will love in the future》

作者在文中根据自己多年的行业经验,提出了几点建议,并结合自身的经历,对之一一说明。

  1. Never compromise on code quality(从不对代码质量妥协)
  2. Always document and write code comments(总是编写文档和代码注释)
  3. Don’t reinvent the wheel, unless you ensure it is maintainable(不要重复造轮子,除非你能确保它是可维护的)
  4. Always test your codebase(总是测试你的代码库)
  5. Keep learning(保持学习)

Tip

最近学到什么新技巧(:зゝ∠),就随意分享个 windows 10 多窗口和多桌面的快捷键吧。

多桌面快捷键
win + tab:打开多桌面视图
ctrl + win + ← 或 ctrl + win + →:快速左右切换虚拟桌面

多窗口快捷键
win + ↑:上分屏
win + ↓:下分屏
win + ←:左分屏
win + →:右分屏

Share

本周分享:

  • 《JVM内存结构 VS Java内存模型 VS Java对象模型》
  • 《再有人问你Java内存模型是什么,就把这篇文章发给他。》

ARTS打卡:第十三周

发表于 2019-06-23 | 分类于 ARTS

ARTS打卡:第十三周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

48. Rotate Image(Medium)

You are given an n x n 2D matrix representing an image.

Rotate the image by 90 degrees (clockwise).

Note:

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

Example 1:

Given input matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],

rotate the input matrix in-place such that it becomes:
[
[7,4,1],
[8,5,2],
[9,6,3]
]

Example 2:

Given input matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],

rotate the input matrix in-place such that it becomes:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]

@DevDocsleetcode.com/problems/rotate-image

大致题意

给一个 n×n 的2D矩阵,要求将这个矩阵顺时针旋转90°
注意: 需要原地旋转,意味着要直接修改输入的2D矩阵,不允许借助其他的矩阵来完成旋转。

解题思路

我们可以通过仔细观察 example 的示例,发现其中规律:

  1. n×n的2D矩阵是一个二维数据,旋转90°,相当于逐层旋转90°,如图所示:
    旋转
    1层和2层分别顺时针旋转90°。
  2. 单层旋转的时候,假设输入的2D矩阵是 a,那么顺时针旋转90°时,矩阵外层的四个角的元素值替换顺序为:a[x][x] -> a[x][y] -> a[y][y] -> a[x][x](x 为起点下标,y为终点下标)。
    那么对于外层□来说,只需依次对上边元素进行旋转90°即可,所以我们可以推导出:a[x][x + offset] -> a[x + offset][y] -> a[y][y - offset] -> a[x][x + offset],其中 offset 是偏移量,要满足 offset < 当前外层正方形的长度 - 1。
  3. 外层向内层递进的时候,边长度都减 2。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 核心思路:
* n × n 的矩阵
* 由最外层到最里层,一层层依次进行旋转。
* <p>
* 可以观察的规律是
* 1. 每层递进时,矩阵边长度都 -2
* 2. a[x][x + offset] -> a[x + offset][y] -> a[y][y - offset] -> a[x][x + offset] 其中 offset 是偏移量,要满足 offset < 当前矩阵边长度 -1
*
* @param matrix
*/
public void rotate(int[][] matrix) {
int x = 0;
int y = matrix.length - 1;
int currLength = matrix.length;
while (x < y) {
int offset = 0;
while (offset < currLength - 1) {
int topLeft = matrix[x][x + offset];
matrix[x][x + offset] = matrix[y - offset][x];
int topRight = matrix[x + offset][y];
matrix[x + offset][y] = topLeft;
int bottomRight = matrix[y][y - offset];
matrix[y][y - offset] = topRight;
matrix[y - offset][x] = bottomRight;
++offset;
}
++x;
--y;
currLength -= 2;
}
}

运行结果与复杂度分析

结果:
Runtime: 0 ms, faster than 100.00% of Java online submissions for Rotate Image.
Memory Usage: 36.1 MB, less than 99.73% of Java online submissions for Rotate Image.

复杂度分析:
时间复杂度:O(n²)
空间复杂度:O(n)

Review

本周阅读:

  1. 《Top 10 Methods for Java Arrays》
    文中列举了Java Array的十大方法。它们是stackoverflow中投票最多的问题。
  2. 《Why String is immutable in Java?》
    文中从内存,同步和数据结构的角度说明了 String 为什么设计为不可变的。
    拓展阅读:《JVM Run-Time Data Areas》
  3. Learn basics of Version Control & Git Commands in less than 10 minutes.
    作者在文中介绍了版本控制的概念、用途和术语以及一些基础的练习方式,并列举一些流行的版本控制系统。
    其中作者着重介绍了最流行的版本控制系统 Git,在文中列举了 Git 的基础命令,并写下了有关 Git 的简单使用教程。

这三篇文章都挺简单易懂的,感觉兴趣的小伙伴们,可以通过查看原文,了解更多详情。

Tip

本周分享有关 firewalld 的一些命令:

  1. firewalld的基本使用
    启动: systemctl start firewalld
    查看状态: systemctl status firewalld
    停止: systemctl disable firewalld
    禁用: systemctl stop firewalld
  2. systemctl是CentOS7的服务管理工具中主要的工具,它融合之前service和chkconfig的功能于一体。
    启动一个服务:systemctl start firewalld.service
    关闭一个服务:systemctl stop firewalld.service
    重启一个服务:systemctl restart firewalld.service
    显示一个服务的状态:systemctl status firewalld.service
    在开机时启用一个服务:systemctl enable firewalld.service
    在开机时禁用一个服务:systemctl disable firewalld.service
    查看服务是否开机启动:systemctl is-enabled firewalld.service
    查看已启动的服务列表:systemctl list-unit-files|grep enabled
    查看启动失败的服务列表:systemctl –failed
  3. 配置firewalld-cmd
    查看版本: firewall-cmd –version
    查看帮助: firewall-cmd –help
    显示状态: firewall-cmd –state
    查看所有打开的端口: firewall-cmd –zone=public –list-ports
    更新防火墙规则: firewall-cmd –reload
    查看区域信息: firewall-cmd –get-active-zones
    查看指定接口所属区域: firewall-cmd –get-zone-of-interface=eth0
    拒绝所有包:firewall-cmd –panic-on
    取消拒绝状态: firewall-cmd –panic-off
    查看是否拒绝: firewall-cmd –query-panic
  4. 开启端口
    添加: firewall-cmd –zone=public –add-port=80/tcp –permanent (–permanent永久生效,没有此参数重启后失效)
    重新载入: firewall-cmd –reload
    查看: firewall-cmd –zone=public –query-port=80/tcp
    删除: firewall-cmd –zone=public –remove-port=80/tcp –permanent

转自:CentOS7使用firewalld打开关闭防火墙与端口。

Share

本周分享耗子叔的一篇文章:《如何超过大多数人》。

ARTS打卡:第十二周

发表于 2019-06-15 | 分类于 ARTS

ARTS打卡:第十二周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

36. Valid Sudoku(Medium)

Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

  1. Each row must contain the digits 1-9 without repetition.
  2. Each column must contain the digits 1-9 without repetition.
  3. Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.

数独
A partially filled sudoku which is valid.

The Sudoku board could be partially filled, where empty cells are filled with the character '.'.

Example 1:
Input:
[
[“5”,”3”,”.”,”.”,”7”,”.”,”.”,”.”,”.”],
[“6”,”.”,”.”,”1”,”9”,”5”,”.”,”.”,”.”],
[“.”,”9”,”8”,”.”,”.”,”.”,”.”,”6”,”.”],
[“8”,”.”,”.”,”.”,”6”,”.”,”.”,”.”,”3”],
[“4”,”.”,”.”,”8”,”.”,”3”,”.”,”.”,”1”],
[“7”,”.”,”.”,”.”,”2”,”.”,”.”,”.”,”6”],
[“.”,”6”,”.”,”.”,”.”,”.”,”2”,”8”,”.”],
[“.”,”.”,”.”,”4”,”1”,”9”,”.”,”.”,”5”],
[“.”,”.”,”.”,”.”,”8”,”.”,”.”,”7”,”9”]
]
Output: true

Example 2:
Input:
[
[“8”,”3”,”.”,”.”,”7”,”.”,”.”,”.”,”.”],
[“6”,”.”,”.”,”1”,”9”,”5”,”.”,”.”,”.”],
[“.”,”9”,”8”,”.”,”.”,”.”,”.”,”6”,”.”],
[“8”,”.”,”.”,”.”,”6”,”.”,”.”,”.”,”3”],
[“4”,”.”,”.”,”8”,”.”,”3”,”.”,”.”,”1”],
[“7”,”.”,”.”,”.”,”2”,”.”,”.”,”.”,”6”],
[“.”,”6”,”.”,”.”,”.”,”.”,”2”,”8”,”.”],
[“.”,”.”,”.”,”4”,”1”,”9”,”.”,”.”,”5”],
[“.”,”.”,”.”,”.”,”8”,”.”,”.”,”7”,”9”]
]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8’s in the top left 3x3 sub-box, it is invalid.

Note:

  • A Sudoku board (partially filled) could be valid but is not necessarily solvable.
  • Only the filled cells need to be validated according to the mentioned rules.
  • The given board contain only digits 1-9 and the character ‘.’.
  • The given board size is always 9x9.
@DevDocsleetcode.com/problems/valid-sudoku

题意如下:
判断 9×9 的数独板是否有效,只需要根据以下规则验证已填充的单元格即可:

  1. 每行必须包含数字1-9而不重复。
  2. 每列必须包含数字1-9而不重复。
  3. 网格的9个3x3子框中的每一个必须包含数字1-9而不重复。
  4. 数独板可以部分填充,其中空单元格填充字符'.'。

注意:

  • 数独板(部分填充)可能是有效的,但不一定是可解的。
  • 只需要根据上述规则验证填充的单元格。
  • 给定的数独板只包含数字1-9和字符'.'。
  • 给定的数独板大小一直都是 9×9 的。

解题思路:
按照题目说的规则和注意事项来看,解题的核心思路在于如何快速的判断出一个字符在当前行且当前列且当前所处的 3×3 小格内有无重复。
那么我们可以联想到借助哈希表的特性,来快速判断字符有无重复。

代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 数独的特性:
* 1. Each row must contain the digits 1-9 without repetition.
* 2. Each column must contain the digits 1-9 without repetition.
* 3. Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.
* <p>
* 所以解决这道题的核心思路在于:如何快速的判断出一个字符在当前行且当前列且当前所处的 3×3 小格内有无重复
* <p>
* 方法:借助哈希表(Set)的特性,快速判断字符有无重复
*
* @param board
* @return
*/
public boolean isValidSudoku(char[][] board) {
// 这里采用 Set 集合,
// 利用 Set 集合的特性:
// 1. 集合内元素不重复
// 2. Set.add(a):如果元素 a 不存在,加入到集合中,并返回 true,反之,则返回 false 且不加入到集合中
Set<String> seen = new HashSet<>();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char number = board[i][j];
// 依照题意,数独的空单元格可以用 '.' 来填充,所以这里要忽略 '.'
if (number != '.') {
// 这里为了便于阅读与理解,我们可以将 board[i][j] 的值进行处理
// 假设 board[0][0] = 1,那么 "1 in row 0" 这个字符串值就可以表示 board[0][0] 在当前行的值;
// "1 in column 0" 这个字符串值就可以表示 board[0][0] 在当前列的值;
// "1 in block 0-0" 这个字符串值就可以表示 board[0][0] 在当前 3×3 小格内的值。
// 这样就不需要借助额外的 set 集合来区分 行、列以及 3×3 的小格了。
// 当然也可以使用三个 set 集合,分别进行区分,不过我觉得现在的实现方法,可读性更高,代码更简短。
if (!seen.add(number + " in row " + i)
|| !seen.add(number + " in column " + j)
|| !seen.add(number + " in block " + i / 3 + "-" + j / 3)) {
return false;
}
}
}
}
return true;
}

结果:
Runtime: 4 ms, faster than 59.98% of Java online submissions for Valid Sudoku.
Memory Usage: 43.3 MB, less than 94.33% of Java online submissions for Valid Sudoku.

复杂度分析:
时间复杂度:O(1),数独的总格数固定是81格,也就是循环遍历次数是81次,是常数阶。
空间复杂度:O(1),额外使用的空间也是81,是常数阶。

Review

本周阅读的是:6 Science-Backed Strategies to Avoid Choking Under Pressure(6个有科学依据的策略来避免在压力下窒息)。

作者在文中说明了在压力下,会产生焦虑,这个过程既是生理上的,也是心理上的,在高风险的情况下,会引起神经反应,例如注意力分散、记忆力丧失和运动功能丧失,所有这些都会影响自身表现。

为此,作者在文中列举了5个有科学依据的策略来避免在压力下窒息:

  1. Don’t think too hard.(别想的太难)
  2. Practice under pressure.(在压力下练习)
  3. Pretend like you’ve already won.(假装你已经赢了)
  4. Tell yourself that you’re in control.(告诉自己一切尽在掌握中)
  5. Give yourself a pep talk.(自我激励)

感兴趣的小伙伴们,可以查看原文获取更多详情哟~(前提是要有梯子(:з」∠))。

ps: 虽然作者的标题写的是有6个策略,但我反复数了下,就只有5个策略(:з」∠)。

Tip

本周分享:Jenkins 子节点构建配置

Share

本周分享梁大在知识星球发的一个帖子,写的真好!
服务端思维
星球是付费,感兴趣的小伙伴们自行斟酌哈~

Jenkins 子节点构建配置

发表于 2019-06-10 | 分类于 Jenkins

Jenkins 子节点构建配置

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,提供超过1000个插件来支持构建、部署、自动化, 满足任何项目的需要。

本文适用于对 Jenkins 有所了解人群。

配置子节点

  1. 进入jenkins服务器的首页,点击左边的系统管理,如下图所示:
    image1
  2. 然后点击右边的管理节点,如下图所示:
    image2
  3. 继续点击左边的新建节点,然后按如下图操作,最后点击OK:
    image3
  4. 点击OK后,进入到节点的配置界面,进行配置,如下图:
    image4

创建用户

登录远程子节点机器,使用以下命令,创建一个新用户:

1
# adduser xxx

这里我们使用root用户,就不创建一个新用户了。

生成xxx用户的ssh密钥

如果是使用新创建的用户,则需要先切换至新用户:

1
su - xxx

使用以下命令生成ssh密钥:

1
$ ssh-keygen -t rsa

一路回车默认空密码即可,最终会在/home/xxx/.ssh目录中生成公钥id_rsa.pub和私钥id_rsa这两个文件。

然后将公钥添加到这台主机的 authorized_keys 文件中,并修改权限为600

1
2
3
4
5
// 添加命令
cat id_rsa.pub >> authorized_keys

// 修改权限的命令
chmod 600 authorized_keys

创建 Jenkins Credentials 认证

回到首页,按照下图操作:
image5
image6
image7

第三步也可以像下图一样操作,不过使用的是主节点的私钥,然后将主节点生成的公钥加入到子节点的 authorized_keys 文件中;生成秘钥和添加公钥的命令,请参考生成xxx用户的ssh密钥。
image8

继续节点信息配置

进入节点配置界面,继续节点配置:
9
10
回到jenkins服务首页,在左边下方出现,如图所示,即为成功了:
image11

将Job关联至子节点

  1. 选择你想关联的 Job,进入配置页面,如下图进行操作:
    12
    标签就是子节点配置页面中填写的标签
  2. 配置 maven
    13
  3. 配置SSH Server
    1. 系统管理->系统设置->Publish over SSH
    2. 新增 SSH Server 配置
      14
  4. 在构建设置配置构建后操作
    16

到此,我们的 Jenkins 子节点配置就大功告成了。

参考链接:

  • Jenkins子节点构建配置
  • jenkins通过SSH Publishers自动构建发包到远程服务器

Apache JMeter 的使用入门

发表于 2019-06-07 | 分类于 Apache JMeter

Apache JMeter 的使用入门

简介

Apache JMeter 是一款开源的 Java 应用程序,用于加载测试功能行为和度量性能。

安装步骤

  1. 下载地址:https://jmeter.apache.org/download_jmeter.cgi
  2. 下载文件:
    JMeter 软件压缩包

  3. 压缩包是 Linux 和 Windows 通用的,直接解压即可。

基本使用步骤

  1. 找到文件解压路径,进入对应 bin 目录下,windows 就直接点击运行 jmeter.bat
    1
  2. 添加一个线程组
    2
  3. 添加完成之后,先设置这两项
    3
  4. 添加一个http请求
    4
  5. 填入配置信息
    5
  6. 点击保存,保存的文件路径自己指定,保存完之后,是一个jmx文件
    6
  7. 添加查看结果树
    7
    还可以添加表格结果、图形结果和聚合结果等。
  8. 启动测试
    8

更多操作指南

查看官网的操作指南
9

ARTS打卡:第十一周

发表于 2019-06-07 | 分类于 ARTS

ARTS打卡:第十一周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

1. Two Sum(Easy)

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

@DevDocsleetcode.com/problems/two-sum

大致题意:
给一个整形数组,返回两个数组下标,两个数组下标对应的值相加,要等于目标值。
你可以假设每一个输入的数组,只有一种答案,并且你不能使用同一个元素两次。

这种题目,一眼看上去,是可以用暴力破解的,不过暴力破解虽然能解决问题,但我们这种三好青年,是有追求,有梦想的,用最少的时间和空间去解决问题,才是我们的终极目标。

解题思路一:暴力破解,双重遍历
这种解法就简单直接了,双重循环遍历即可。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 暴力破解,双重遍历
*
* @param nums 输入数组
* @param target 目标值
* @return
*/
public static int[] twoSum1(int[] nums, int target) {
if (nums.length < 1) {
throw new IllegalArgumentException("No two sum solution");
}
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
throw new IllegalArgumentException("No two sum solution");
}

结果:
Runtime: 18 ms, faster than 38.89% of Java online submissions for Two Sum.
Memory Usage: 38.2 MB, less than 94.24% of Java online submissions for Two Sum.

从结果上看,我们还是有很大优化空间的。

复杂度分析:
时间复杂度:O(n²)
空间复杂度:O(1)

解题思路二:借助哈希表,空间换时间,优化遍历
可以利用哈希表的特性,减少遍历次数,详细实现不难,直接看代码即可。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 借助哈希表进行优化
*
* @param nums 输入数组
* @param target 目标值
* @return
*/
public static int[] twoSum(int[] nums, int target) {
// 边界条件
if (nums.length < 1) {
throw new IllegalArgumentException("No two sum solution");
}
// 创建一个 HashMap,key 为数组的值,value 为值对应的数组下标
// 用于记录遍历过的数组的值与下标,便于快速查找到所需的值
Map<Integer, Integer> hash = new HashMap<>(16);
// 循环遍历数组
for (int i = 0; i < nums.length; i++) {
// 我们的目的是找到符合 nums[i] + nums[j] = target 的 i 和 j,t 就是 nums[j]
int t = target - nums[i];
// 查看 t 是否在已访问过的数组值内 即哈希表的 keys 包不包含 t,
// 加上由于题目要求同一个元素不能使用两次,所以还需满足当前下标 i 不能与 hash.get(t) 的值一致
// 即 hash.containsKey(t) && hash.get(t) != i
if (hash.containsKey(t) && hash.get(t) != i) {
return new int[]{hash.get(t), i};
}
// 记录已访问过的,数组的值与下标
hash.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}

结果:
Runtime: 2 ms, faster than 99.20% of Java online submissions for Two Sum.
Memory Usage: 37.4 MB, less than 99.67% of Java online submissions for Two Sum.

复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n)

Review

本周阅读是:《Top 10 Mistakes Java Developers Make》。

作者在文中总结了 Java 开发人员经常犯的十大错误:

  1. Convert Array to ArrayList(Array 转 ArrayList)
  2. Check If an Array Contains a Value(检查数组是否包含某个值)
  3. Remove an Element from a List Inside a Loop(从 List 的循环中删除 List 的元素)
  4. Hashtable vs HashMap
  5. Use Raw Type of Collection(使用原始类型的集合)
  6. Access Level(访问级别)
  7. ArrayList vs. LinkedList
  8. Mutable vs. Immutable(可变与不可变)
  9. Constructor of Super and Sub(父类和子类的构造函数)
  10. “” or Constructor?(””或构造函数)

作者在文中对每点错误都进行了详细的分析,并给出了对应的解决方案或说明。
文中的英语其实不难理解,感兴趣的小伙伴们,可以通过原文了解更多的详情。
如果实在不想看英语,那可以看看这篇译文:Java开发人员最常犯的10个错误。

Tip

本周分享:Apache JMeter 的使用入门。

Share

本周分享猪猪大佬的公众号-工匠人生中的一篇文章:《如何将长URL转换为短URL?》
通过这篇文章,我们可以了解到:

  • 如何将长URL生成短URL
  • 短地址从URL输入到页面展现到底发生了什么?
  • 短连接的设计思路
  • 发号器的设计思路
  • 知乎关于生成短地址的讨论

这里摘取文中提到的,一部分参考文章的地址:

  1. 短网址(short URL)系统的原理及其实现
  2. 短 URL 系统是怎么设计的?
  3. How do I create a URL shortener?

ARTS打卡:第十周

发表于 2019-06-02 | 分类于 ARTS

ARTS打卡:第十周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

66. Plus One(Easy)

Given a non-empty array of digits representing a non-negative integer, plus one to the integer.

The digits are stored such that the most significant digit is at the head of the list, and each element in the array contain a single digit.

You may assume the integer does not contain any leading zero, except the number 0 itself.

Example 1:

Input: [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123.

Example 2:

Input: [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321.

@DevDocsleetcode.com/problems/plus-one

题意:

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

解题思路:

这道题就非常简单了,利用数学的加法运算思想,倒序遍历数组,如果当前值不等于9,直接加1,然后返回数组,若等于 9,9 + 1 = 10,需要进位,把当前值设为 0,继续遍历数组,依次类推,若最后数组能遍历完,则表明输入数组的值全为9,+1 后,位数发生变化,结果一定是首位为 1,余位为 0 的数组, 所以要重新创建一个数组 result,大小为输入数组的长度 +1,result[0] 设为 1,然后返回数组 result。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int[] plusOne(int[] digits) {
//倒序遍历
for (int i = digits.length - 1; i >= 0; i--) {
// 不为9,则直接+1,返回结果数组
if (digits[i] != 9) {
digits[i] += 1;
return digits;
}else {
// 为9,9+1=10,需进位,当前值设为 0
digits[i] = 0;
}
}
// 输入数组的值全为 9,+1 后位数也要 +1,结果一定是首位为 1,余位为 0 的数组
int[] result = new int[digits.length + 1];
// 利用整形数组初始化后,值为0的特性,只需对数组首位赋值
result[0] = 1;
return result;
}
}

结果:
Runtime: 0 ms, faster than 100.00% of Java online submissions for Plus One.
Memory Usage: 34.2 MB, less than 100.00% of Java online submissions for Plus One.

复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n)

283. Move Zeroes(Easy)

Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.

Example:

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

Note:

You must do this in-place without making a copy of the array.
Minimize the total number of operations.

@DevDocsleetcode.com/problems/move-zeroes

题意:

给定一个数组 nums,写一个函数将所有 0 移动至数组末尾,同时保持数组内非零元素的相对顺序。
注意:

  1. 必须在原数组上操作,不能拷贝额外的数组
  2. 尽量减少操作次数

解题思路:

由于题目要求要在原数组上操作,不能用额外的数组空间,那我们可以先从如何复用原数组空间出发,结合要求,去解题。

因为要将所有 0 移动至数组末尾且不可改变非零元素的相对位置,那么逐步交换 0 与非零元素的位置即可实现;考虑到要复用原数组空间且减少操作步骤,我们可以不交换位置,逐步将非零元素覆盖 0,直至所有 0 都被覆盖,最后末尾补零即可。

细节请参看代码实现

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void moveZeroes(int[] nums) {
// 边界条件
if (nums == null || nums.length == 0) {
return;
}
// 设置一个锚点,从 0 开始
int anchor = 0;
// 遍历输入数组
for (int num : nums) {
// 判断当前元素是否为非零元素
if (num != 0) {
//不为零,则用当前的值覆盖锚点对应的数组的值,并将锚点往前移动一位
nums[anchor++] = num;
}
// 为零,锚点不移动,数组继续遍历,依次类推,就能实现非零元素覆盖 0 元素
}
// 经过上述遍历覆盖之后,锚点之前,皆为非零元素,锚点之后,应该全为零
while (anchor < nums.length) {
nums[anchor++] = 0;
}
}

结果:
Runtime: 0 ms, faster than 100.00% of Java online submissions for Move Zeroes.
Memory Usage: 36.8 MB, less than 99.94% of Java online submissions for Move Zeroes.

复杂度分析:
时间复杂度:O(n)
空间复杂度:O(1)

Review

本周阅读是:《Introduction to Concurrency in Spring Boot》

作者在文中提供了一些在 Spring Boot 中的处理多线程的实用建议,以及如何避免它可能产生的问题。

  1. Spring Boot 并发基础知识
    在考虑Spring Boot应用程序中的并发性时,值得考虑的关键领域是:
    • 最大线程数:分配给应用处理请求的最大线程数。
    • 共享外部资源:调用外部共享资源,例如数据库。
    • 异步方法调用:这些方法调用在等待响应时将线程释放回线程池。
    • 共享内部资源:调用内部共享资源,例如缓存和潜在的应用共享状态。
  2. Spring Boot Application 中的最大线程数
    首先我们应该注意,正在处理的线程是有限的。
    如果使用的是tomcat,你可以使用属性server.tomcat.max-threads去控制希望允许多少线程,缺省值是 0,意味着使用 Tomcat 的缺省值 200。
  3. 使用异步方法调用去解决共享外部资源花费大量等待时间的问题
  4. 在 Spring Boot 中进行异步调用
    在主程序类中的注解 @SpringApplication 之上加注解@EnableAsync,加上之后,可以在你的 Service 方法上加 @Asyn,并将返回结果改为CompletableFuture<>,这样@Asyn的方法将后台线程池中运行,合理使用,可以降低性能损耗,提高服务效应效率。
  5. 控制内部资源,避免与共享相关问题的最佳建议是不要共享它们。
  6. 如果要共享某些状态,以下是一些建议:
    • 处理不可变的对象,如果对象是不可变的,将会避免许多并发问题。如果你需要改变一些东西,那就创建一个新的对象。
    • 了解你的集合是否是线程安全的,如果需要并发访问,请使用ConcurrentHashMap或者其他线程安全的解决方法。
    • 不要假设第三方库是线程安全的。大多数代码都不是,并且必须控制对共享状态的访问。

想要了解更多细节的小伙伴们,请点击原文,查看详情。

Tip

本周分享一个在梁大的知识星球中看到的,有关 lombok 的知识点,不知道 lombok 的小伙伴们,可以查看ARTS 第二周分享中 Tip 那小节。
lombok

Share

本周分享:《基于支付场景下的微服务改造与性能优化》,深度好文。

ARTS打卡:第九周

发表于 2019-05-18 | 分类于 ARTS

ARTS打卡:第九周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

350. Intersection of Two Arrays II(Easy)

Given two arrays, write a function to compute their intersection.

Example 1:

Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2,2]

Example 2:

Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [4,9]

Note:

  • Each element in the result should appear as many times as it shows in both arrays.
  • The result can be in any order.

Follow up:

  • What if the given array is already sorted? How would you optimize your algorithm?
  • What if nums1’s size is small compared to nums2’s size? Which algorithm is better?
  • What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once?
@DevDocsleetcode.com/problems/intersection-of-two-arrays-ii

大致题意:

给两个数组,写一个方法计算它们的交集。
注意:

  1. 结果中的每个元素应该出现在两个数组中显示的次数。
  2. 结果可以是任何顺序。

解题思路1:
假设输入的数组为 a、b,输出的数组为 r

  1. 对a、b进行排序
  2. 假设 i 为 a 的起始下标 0,j 为 b 的起始下标 0,k 为结果集的起始下标,从 0 开始
  3. 遍历数组,比较 a[i] 和 b[j],如果 a[i] < b[j],移动下标 i,即 i++;如果 a[i] > b[j],移动下标 j,即 j++;如果 a[i] = b[j],使得 r[k] = b[j],且同时往前移动三个数组的下标。
  4. 以此类推,直至 i 等于数组a的长度且 j 等于数组b的长度,终止循环,数组 r 就是结果
  5. 输出数组 r 可以为 数组 a

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public int[] intersect(int[] nums1, int[] nums2) {
// 边界条件
if (nums1.length == 0) {
return nums1;
}
if (nums2.length == 0) {
return nums2;
}
LinkedList<Integer> results = new LinkedList<>();
// 1. 先排序
Arrays.sort(nums1);
Arrays.sort(nums2);
int i = 0, j = 0, k = 0;
while (i < nums1.length && j < nums2.length) {
if (nums1[i] < nums2[j]) {
i++;
} else if (nums1[i] > nums2[j]) {
j++;
} else {
nums1[k++] = nums1[i];
i++;
j++;
}
}
return Arrays.copyOf(nums1, k);
}

结果:
Runtime: 1 ms, faster than 100.00% of Java online submissions for Intersection of Two Arrays II.
Memory Usage: 35.5 MB, less than 73.31% of Java online submissions for Intersection of Two Arrays II.

复杂度分析:
时间复杂度:O(n)。Array.sort() 时间复杂度是 O(nlogn),遍历是 O(n),时间复杂度主要取决于遍历。
空间复杂度:O(k)。

解题思路2:
因为题目对结果的顺序没有要求,所以我们可以借助 map 来实现。

  1. 假设输入的数组为:数组a和数组b,i,j分别为数组a、b的下标;
  2. 借助一个容器 map,key 为数组的值,value 为值出现的次数和用一个 list 存储结果
  3. 遍历数组a,判断 map 的 key 集合中是否有 a[i],有,则 value + 1,无,则把这个值加入到map中
  4. 遍历数组b,判断 map 的 key 集合中是否有 b[j],且对应的value 是否大于 0,若是,将 b[j] 加入到结果 list 中,并将 value 减一。
  5. 遍历结束,将list转为数组,输出结果。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public static int[] intersect(int[] nums1, int[] nums2) {
// 边界条件
if (nums1.length == 0) {
return nums1;
}
if (nums2.length == 0) {
return nums2;
}
Map map = new HashMap<Integer, Integer>(16);
List<Integer> result = new ArrayList<>();
for (int i : nums1) {
// 判断 map 的 key 集合中是否有 a[i],有,则 value + 1,无,则把这个值加入到map中
if (map.containsKey(i)) {
map.put(i, (int) map.get(i) + 1);
} else {
map.put(i, 1);
}
// 可以换成 java 8 的写法
//map.put(nums1[i], (int) map.getOrDefault(nums1[i], 0) + 1);
// 或
//map.merge(nums1[i], 1, (a, b) -> (int) a + (int) b);
}
for (int i : nums2) {
//判断 map 的 key 集合中是否有 b[j],且对应的value 是否大于 0,若是,将 b[j] 加入到结果 list 中,并将 value 减一
if (map.containsKey(i) && (int) map.get(i) > 0) {
result.add(i);
map.put(i, (int) map.get(i) - 1);
}
}
int[] r = new int[result.size()];
for (int i = 0; i < r.length; i++) {
r[i] = result.get(i);
}
return r;
}

结果:
Runtime: 3 ms, faster than 54.64% of Java online submissions for Intersection of Two Arrays II.
Memory Usage: 35.7 MB, less than 66.55% of Java online submissions for Intersection of Two Arrays II.

Review

这周分享:《Spring Boot – Best Practices》
作者在文中结合了自己的工作经验和其他springboot专家著作,给出了如下15条实践建议:

  1. 使用自动配置
  2. 使用Spring Initializr来开始一个新的Spring Boot项目
  3. 考虑为常见的组织问题创建自己的自动配置
  4. 正确设计你的代码目录结构
  5. 保持你的 @Controller 的整洁和专一
  6. 围绕业务功能来构建你的 @Service
  7. 使数据库独立于核心业务逻辑之外
  8. 保持核心业务逻辑独立于 spring boot
  9. 推荐使用构造函数
  10. 熟悉并发模型
  11. 加强配置管理的外部化
  12. 提供全局异常处理
  13. 使用日志框架
  14. 测试你的代码
  15. 使用切片测试,让测试更容易,更专注

感兴趣的小伙伴们,可以点击原文,查看更多的详情~

Tip

这周分享一个在工作上用到的抓包工具:Wireshark,可以直接在网上下载,是个免费软件。
下载地址:https://www.wireshark.org/download.html
使用方法可以直接 google 或者度娘又或者看我在 Share 章节分享的笔记。

Share

本周分享:Wireshark使用详解以及HTTP抓包。

该笔记部分内容是我结合网上许多资料而来的,记录时间比较久远了,已经不清楚是从哪几篇文章上摘抄下来的了,如有侵权,联系删除。

ARTS打卡:第八周

发表于 2019-05-12 | 分类于 ARTS

ARTS打卡:第八周

每周完成一个ARTS:

  1. Algorithm:每周至少做一个 leetcode 的算法题
  2. Review:阅读并点评至少一篇英文技术文章
  3. Tip:学习至少一个技术技巧
  4. Share:分享一篇有观点和思考的技术文章

Algorithm

136. Single Number(Easy)

Given a non-empty array of integers, every element appears twice except for one. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,1]
Output: 1

Example 2:

Input: [4,1,2,1,2]
Output: 4

@DevDocsleetcode.com/problems/single-number

题意:
给一个不空的整型数组,除一个元素之外,其余每个元素都会出现两次,找到那个独立的元素。
注意:你的算法应该具有线性运行时复杂度。你能不用额外的内存来实现它吗?

解题思路1:
根据题意,我们可以知道,输入是一个不为空的整型数组,且这个数组长度一定是奇数的,且那个不重复的元素一定是奇数位的元素,那么我们可以先对数组进行排序,然后遍历数组,假设这个数组为 nums,当 nums[i] != nums[i + 1] && nums[i] != nums[i - 1] 时,nums[i] 即为那个不重复的元素,当然首尾元素要单独进行判断。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int singleNumber(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
Arrays.sort(nums);
if (nums[0] != nums[1]) {
return nums[0];
}
for (int i = 2; i < nums.length - 1; i += 2) {
System.out.println(i);
if (i != nums.length - 1 && nums[i] != nums[i + 1] && nums[i] != nums[i - 1]) {
return nums[i];
}
}
return nums[nums.length - 1];
}

结果:
Runtime: 3 ms, faster than 42.26% of Java online submissions for Single Number.
Memory Usage: 37.9 MB, less than 77.61% of Java online submissions for Single Number.

解题思路2:
还有一种解算,我在讨论区看到的,着实让我眼前一亮,从而打开了新世界的大门。
利用 XOR 逻辑异或去做,因为 0 ^ N = N,N ^ N = 0,所以只需以0为初始值,去遍历数组,进行异或运算,最后就能得到那个唯一的不同值。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
/**
* 哇,这个解法完全没想到,打开新世界的大门
* 使用 XOR 逻辑异或 去做
* 0 ^ N = N
* N ^ N = 0
* 所以只需遍历进行异或,就能得到那个唯一的不同值
* @param nums
* @return
*/
public int singleNumber(int[] nums) {
int a = 0;
for (int i : nums) {
a ^= i;
}
return a;
}
}

结果:
Runtime: 0 ms, faster than 100.00% of Java online submissions for Single Number.
Memory Usage: 38.2 MB, less than 73.27% of Java online submissions for Single Number.

时间复杂度:O(n)
空间复杂度:O(1)

Review

本周分享一篇在 Medium 上看到的文章:《What I learned from doing 1000 code reviews》。
作者在这篇文章中,分享了自己在 LinkedIn 工作期间,进行大量代码审查后得出 code review 的四点建议:

  1. 当出现问题时,要抛出异常。

  2. 尽可能使用最具体的类型。

  3. 使用 Optional 代替 nulls。

  4. 尽可能使用 “Unlift” 方法。

Tip

本周分享一个 IDEA 的插件:Sequence Diagram。
安装步骤:Setting(Ctrl + Shift + s)->Plugins->Marketplace,在插件商店直接搜索 SequenceDiagram,然后安装插件,重启 IDEA。
效果如下:

鼠标右键点击,选择 Sequence Diagram
Sequence Diagram

就可以看到自动生成的时序图了
时序图

更多详情,可以查看 SequenceDiagram 的官网:SequencePlugin。

如果无法通过插件商店下载,可以在这里https://plugins.jetbrains.com/plugin/8286-sequencediagram手动下载,然后自行安装。

Share

这周分享一篇梁大的文章:《人人都是 API 设计者:我对 RESTful API、GraphQL、RPC API 的思考》

123
渴望飞的哺乳类

渴望飞的哺乳类

愿你仗剑走天涯,归来仍是少年

28 日志
5 分类
5 标签
© 2019 渴望飞的哺乳类
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4
本站访客数 人次 访问总量 次