questions

  1. 说说dubbo服务上下线时你们是如何处理的?
  2. dubbo的协议有哪些,哪些使用场景?
  3. dubbo使用的系列化框架是什么?
  4. dubbo服务容错机制?
  5. socket协议的几种状态,socket协议的三次握手,为什么是三次握手?
  6. 说说nio和io有什么不同,nio有哪些场景?
  7. http请求有哪些方法?
  8. jdk1.8有哪些新特性,使用过哪些?
  9. 谈谈对GC的理解,对G1有了解吗?
  10. Collection.sort底层默认使用哪种排序算法,说说你熟悉的排序算法有哪些?
  11. 基本数据类型,占用字节,包装类,为什么会有包装类?
  12. 多线程你们在项目中怎么用的?
  13. jdk1.8中Stream在并行处理时,适合哪些情景,不适合哪些情景?(第一次遇到这样问的,没回答上来)
    适合计算密集型,不适合IO密集型。因为Stream被分配到的是核心线程池,IO密集型会造成线程阻塞。
  14. 你们reids在使用中,过期时间是怎么设置的,分两步设置不能保证原子性,怎么解决的?
  15. 写一下二分查找算法?
  16. 写一个单例?(DCL)?
  17. 说说dubbo和spring cloud有什么区别?
  18. 你们数据库是怎么优化的,说说实例?
  19. 了解redis吗,说说redis基本数据结构,有哪些常用的指令?

1、说说 dubbo 的执行流程
2、redis 基本数据结构,你们是怎么用的(讲了下 5 分钟发帖 10 次)
3、数据库连接池了解吗?
4、生产环境项目打印日志出现了时间长的现象,你怎么排查
5、接口出现频繁 GC ,怎么排查


1 线程池用过哪些,在项目中是怎么使用的

2 sql优化怎么做的,哪些情况触发不了索引 , sql优化除了索引还要看哪些指标

3 redis有没有遇到数据库满了的情况

4 说说redis cluster

5 springmvc springboot springcloud的区别

6 对restful有了解吗,弹弹restful

7 说说对spring的理解

8 @Autowired和@Resource的区别

9 设计模式有哪些了解,说说设计模式遵循的原则

10 交给你一个项目,怎么推进

11 通过哪些途径学习,看些什么书

12 总结下自己


linux系统

linux三种网络模式

在我们安装vmware的时候,vmware会为三种网络模式各自创建一个虚拟机网络, 其中 VMnet0(表示桥接模式) VMnet8(NAT模式) VMnet1(仅主机模式)


桥接模式
和主机一样,也会占用一个局域网中的ip。信息的发送和接受,虚拟网络适配器和主机的物理网络适配器进行交换,虚拟网络适配器可以通过主机的物理网络适配器访问外部网络。在局域网中的其它主机能够识别到发送信息的ip为该虚拟机的ip。

NAT模式
虚拟机发送数据时,NAT虚拟机网络适配器会以主机的名义将数据包裹发送出去,接收时通过特殊的标识识别。在外部网络中并不知道当前虚拟机的存在。

主机模式
仅可以和主机通信,无法访问外部网络。


一篇介绍的很形象的文章


centos7之动态ip与静态ip
centos7获取ip地址的方法主要有两种 1:动态获取  2:设置静态ip

我们一般通过VMWare安装完centos7后,可以使用命令ip addr查看虚拟机的ip地址。但是刚安装完的centos7有些没有设置ip地址,我们可以通过下面两种方式来设置ip地址。

1. 动态获取ip(前提是路由器已经开启了DHCP(动态主机设置协议))
a: 需要修改网卡的配置文件 /etc/sysconfig/network-scripts/ifcfg-ens33 其中ifcfg-ens33为网卡名。修改该文件的两处地方既可。
(1) bootproto=dhcp
(2) onboot=yes
b: 重启下网络服务
[root@mini ~]# systemctl restart network
c: 通过 ip addr 命令可以看到ens33的网卡已经有相应的ip地址啦
d: 通过 ping www.baidu.com 查看网络是否通畅

在VMware里,点击'编辑'-'虚拟网络编辑器'
2. 配置静态ip地址(网络模式在nat模式下)
a: 也是需要修改网卡配置文件 /etc/sysconfig/network-srcipts/ifcfg-ens33 其中ifcfg-ens33为网卡名。修改该文件的两处地方既可。
(1) bootproto=static
(2) onboot=yes
b: 还需要在该网卡配置文件的后面加上几行,分别是ip地址,子网掩码,网关,dns服务器
IPADDR=192.168.25.156 #静态ip
NETMASK=255.255.255.0 #子网掩码
GATEWAY=192.168.1.1 #网关
DNS1=192.168.25.2
DNS2=8.8.8.8 #谷歌的dns服务器
c: 重启下网络服务
[root@mini ~]# systemctl restart network
d: 通过 ip addr 命令可以看到ens33的网卡已经有新的ip地址啦
e: 通过 ping www.baidu.com 查看网络是否通畅

3. 在 /etc/resolv.conf文件里面保存了dns地址,可以通过cat命令查看

在设置静态ip时,子网掩码和网关地址可以在vmware的->编辑->虚拟网络编辑器->NAT设置 中查看,如下图所示

字符串

题目来自leetcode

字符串反转1(344)
/*
题目要求:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组char[]的形式给出。
*/
public void reverseString(char[] s) {
int low = 0;
int high = s.length - 1;
while (low < high) {
char temp = s[low];
s[low++] = s[high];
s[high--] = temp;
}
}
字符串反转2(541)
/*
题目要求:给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转。如果剩余少于 k 个字符,则将剩余的所有全部反转。如果有小于 2k 但大于或等于 k 个字符,则反转前 k 个字符,并将剩余的字符保持原样。
示例:输入: s = "abcdefg", k = 2 输出: "bacdfeg"
*/
public String reverseStr(String s, int k) {
// 计算长度 整数遍历次数 剩余元素个数
int len = s.length();
int lastNum = len % (2 * k);
int num = len / (2 * k);
char[] chars = s.toCharArray();
// 先将前2*k*num个元素做反转
for (int i = 1; i < 2 * num; i += 2) {
int low = (i - 1) * k;
int high = i * k - 1;
reverseChar(chars, low, high);
}
// 将剩余的不到2*k个元素反转
if (lastNum > k) {
reverseChar(chars, 2*k*num, 2*k*num+k-1);
} else {
reverseChar(chars, 2*k*num, len-1);
}
return new String(chars);
}

// 字符数组反转函数
public void reverseChar(char[] chars, int low, int high) {
while (low < high) {
char temp = chars[low];
chars[low ++] = chars[high];
chars[high --] = temp;
}
}
字符串反转3(557)
/*
题目要求: 给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
注意:在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。
示例 1:
输入: "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc"
*/
// 解法:将字符串按照空格拆分成为字符串数组,然后将每一个字符串翻转,最后将反转的字符串拼接到一起。
// 其中字符串的反转采用的方式为先转换为字符数组,然后反转字符数组。
public String reverseWords(String s) {
StringBuilder sb = new StringBuilder();
String[] split = s.split("\\s+");
for (int i = 0; i < split.length ; i++) {
char[] chars = split[i].toCharArray();
int low = 0;
int hig = chars.length - 1;
while (low < hig) {
char temp = chars[low];
chars[low ++] = chars[hig];
chars[hig --] = temp;
}
sb.append(chars);
if (i != split.length - 1) {
sb.append(" ");
}
}
return sb.toString();
}
字符串反转4(917)
/*
题目要求:给定一个字符串 S,返回 “反转后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置发生反转。
示例 1:输入:"ab-cd" 输出:"dc-ba"
示例 2:输入:"a-bC-dEf-ghIj" 输出:"j-Ih-gfE-dCba"
*/
public String reverseOnlyLetters(String S) {
// 双指针
StringBuffer buf = new StringBuffer();
int j = S.length() - 1;
for (int i = 0; i < S.length(); i ++) {
// 位置i上的字符为字母时,可以做交换
if (Character.isLetter(S.charAt(i))) {
while(!Character.isLetter(S.charAt(j))) {
j --;
}
buf.append(S.charAt(j --));
} else {
buf.append(S.charAt(i));
}
}
return buf.toString();
}
字符串反转5(345)
/*
题目要求:编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 1: 输入: "hello" 输出: "holle"
示例 2: 输入: "leetcode" 输出: "leotcede"
说明: 元音字母不包含字母"y"。
*/

// 元音字母集合
final static Set<Character> sets = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));

// 使用双指针,当两端同时为元音字母时,交换两个位置的字符。
public String reverseVowels(String s) {
int left = 0;
int right = s.length() - 1;
char[] chars = s.toCharArray();
while (left <= right) {
while (!judgeVowel(chars[left])) {
left ++;
if (left > s.length() -1) {
break;
}
}
while (!judgeVowel(chars[right])) {
right --;
if (right < 0) {
break;
}
}
if (left > right) {
break;
}
char temp = chars[left];
chars[left++] = chars[right];
chars[right--] = temp;
}
return new String(chars);
}
报数问题(38)
/*
题目要求:
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
说明:上一项报前一项,报数时是按照各数+数字报的。比如:给1报数的是2,报数时时1个1,即为11。给2报数的是3,报的数是11,2个1,即为21。报21时,1个2,1个1,即为1211
*/
// 解法:就是按照规律统计前一项的数字,直到第n项统计出n-1为止。
public String countAndSay(int n) {
String res = "1";
for (int i = 2; i <= n; i ++) {
StringBuffer buf = new StringBuffer();
char ch = res.charAt(0);
int num = 0;
for (int j = 0; j < res.length(); j ++) {
if (ch == res.charAt(j)) {
num ++;
} else {
buf.append(num).append(ch);
ch = res.charAt(j);
num = 1;
}
}
buf.append(num).append(ch);
res = buf.toString();
}
return res;
}
二进制求和(67)
/*
题目要求:
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1 和 0。
示例:
输入: a = "11", b = "1" 输出: "100"
输入: a = "1010", b = "1011" 输出: "10101"
*/
// 从后往前遍历计算,将计算结果拼接到字符串后面,最后将字符串反转
public String addBinaryA(String a, String b) {
StringBuffer buf = new StringBuffer();
int ans = 0;
for (int i = a.length() - 1, j = b.length() - 1; i >= 0 || j >= 0; i--, j--) {
int sum = ans;
// 两个字符串长度可能不同,故先遍历完毕的字符串计算值用0代替
sum += (i >= 0 ? a.charAt(i) - '0' : 0);
sum += (j >= 0 ? b.charAt(j) - '0' : 0);
// 拼接
buf.append(sum % 2);
// 进位
ans = sum / 2;
}
buf.append(ans == 1 ? ans : "");
return buf.reverse().toString();
}
验证回文串(125)
/*
题目要求: 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例:
输入: "A man, a plan, a canal: Panama" 输出: true
输入: "race a car" 输出: false
*/
public boolean isPalindromeA(String s) {
if (s == null || s.trim().length() == 0) {
return true;
}
// 将字符串转小写
String param = s.toLowerCase();
int length = param.length();
int left = 0;
int right = length - 1;
// 左右两个指针遍历,相遇结束
while (left < right) {
// 从左起,定位到第一个字母或是数字
while (left < right && !isSuitable(param.charAt(left))) {
left ++;
}
// 从右起,定位到第一个字母或是数字
while (left < right && !isSuitable(param.charAt(right))) {
right --;
}
if (left >= right) {
return true;
}
// 不符合条件,返回false
if (param.charAt(left ++) != param.charAt(right --)) {
return false;
}
}
return true;
}
// 判定给定的字符是否为字母或数字
public boolean isSuitable(char c) {
return Character.isLetter(c) || Character.isDigit(c);
}
有效括号(20)
/*
题目要求:
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例: 输入: "()" 输出: true
输入: "()[]{}" 输出: true
输入: "(]" 输出: false
输入: "([)]" 输出: false
输入: "{[]}" 输出: true
*/
// 解法:使用栈来操作
public boolean isValid(String s) {
// 规律:对应的括号只有可能在紧邻的一位或者对称的位置找到
// 空字符串直接返回 true
if (s == null || s.trim().length() == 0) {
return true;
}
int length = s.length();
// 非2的偶数倍,直接返回false
if (length % 2 != 0) {
return false;
}
// 通过定义stack来实现,遇到左括号放入到stack中,遇到右括号从stack中弹出元素与之对比
// 这种的扩展性较好,但是要引入HashMap,并且要匹配key,所以时间复杂度和空间复杂度都没下面的好
Stack<Character> stack = new Stack<Character>();
Map<Character, Character> map = new HashMap<Character, Character>();
map.put('(', ')');
map.put('{', '}');
map.put('[', ']');
Set<Character> keys = map.keySet();
for (int j = 0; j < length; j ++) {
char c = s.charAt(j);
if (keys.contains(c)) {
stack.push(c);
} else {
if (stack.isEmpty() || map.get(stack.pop()) != c) {
return false;
}
}
}
// 下面这种时间复杂度和空间复杂度都要由于上面的,但是扩展性不好
/*
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < length; i ++) {
char c = s.charAt(i);
switch(c) {
case '(':
case '{':
case '[':
stack.push(c);
break;
case ')':
if (stack.isEmpty() || stack.pop() != '(') {
return false;
}
continue;
case '}':
if (stack.isEmpty() || stack.pop() != '{') {
return false;
}
continue;
case ']':
if (stack.isEmpty() || stack.pop() != '[') {
return false;
}
continue;

}
}
*/
return stack.isEmpty();
}
无重复字符的最长子串(3)
// 滑动窗口解法
public int lengthOfLongestSubstring(String s) {
int length = s.length();
int ans = 0;
int left = 0;
int right = 0;
Set<Character> set = new HashSet<Character>();
while(left < length && right < length) {
if (!set.contains(s.charAt(right))) {
set.add(s.charAt(right ++));
ans = Math.max(ans, right - left);
} else {
set.remove(s.charAt(left ++));
}
}
return ans;
}
最长回文子串(5)
// 题目要求:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
// 1.要判断一个字符串是否为回文字符串,可以通过下面几种方式来实现
// 第一种:通过栈的后进先出原理,将字符串翻转来对比前后两个字符串是否一致
public static boolean isHuiWen1(String str) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < str.length(); i++) {
stack.push(str.charAt(i));
}
StringBuffer buf = new StringBuffer();
while (!stack.isEmpty()) {
buf.append(stack.pop());
}
if (str.equals(buf.toString())) {
return true;
}
return false;
}
// 第二种: 取消栈的引入,直接通过逆序输出拼接字符串(减少了额外的空间Stack)
public static boolean isHuiWen2(String str) {
StringBuffer buf = new StringBuffer();
for (int i = str.length() - 1; i >= 0 ; i--) {
buf.append(str.charAt(i));
}
if (buf.toString().equals(str)) {
return true;
}
return false;
}
// 第三中: 基于回文串左右两边两个字符相等的规律,定义两个变量做对比(优势是减少了字符串str的遍历次数,时间复杂度降低)
public static boolean isHuiWen3(String str) {
int length = str.length();
int left = 0;
int right = length - 1;
while (left <= right) {
if (!(str.charAt(left) == str.charAt(right))) {
return false;
}
left ++;
right --;
}
return true;
}

// 当前这道题如何实现
public String longestPalindrome(String s) {
// 给定一个字符串s,找到s中最长的回文子串。你可以假设s的最大长度为1000。
int len = s.length();
if (len == 0) {
return "";
}
int resultLen = 1;
String resultStr = s.substring(0, 1);
for (int i = 0; i < len; i ++) {
// 从中间向两边查找对称的回文串
String oddStr = spread(s, len, i, i);
String evenStr = spread(s, len, i, i+1);
String myStr = oddStr.length() >= evenStr.length() ? oddStr : evenStr;
if (myStr.length() > resultLen) {
resultStr = myStr;
resultLen = resultStr.length();
}
}
return resultStr;
}

public String spread(String s, int len, int left, int right) {
int l = left;
int r = right;
while (l >= 0 && r < len && (s.charAt(l) == s.charAt(r))) {
l --;
r ++;
}
return s.substring(l+1, r);
}
压缩字符串(443)
// 题目要求: 给定一组字符串,使用原地算法将其压缩。
// 题目解法
public int compress(char[] chars) {
int anchor = 0; // 描点,定位可以元素
int write = 0; // 写下标,定位写的位置
for (int read = 0; read < chars.length; read ++) {
// 若是当前元素为最后一个元素或者是后一个元素与当前元素不相等,执行写入与统计写入操作
if ((read == chars.length - 1) || (chars[read + 1] != chars[read])) {
// 将anchor位置处的元素写入到write位置处
chars[write ++] = chars[anchor];
// 判断该元素是否需要压缩
if (read > anchor) {
String numStr = read - anchor + 1 + "";
char[] numArr = numStr.toCharArray();
for (char num : numArr) {
chars[write ++] = num;
}
}
// 将锚点移动到下一个元素
anchor = read + 1;
}
}
// 返回覆盖的个数
return write;
}
字符串中的第一个唯一字符(387)
/*
题目要求: 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
说明: 你可以假设字符串只含有小写字母。
示例: s = "leetcode" 返回 0. s = "loveleetcode" 返回 2.
*/
// 第一中解法,遍历两次字符串,哈希表存储每个字符和其出现的个数
public int firstUniqCharA(String s) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i ++) {
if (!map.containsKey(s.charAt(i))) {
map.put(s.charAt(i), 1);
} else {
map.put(s.charAt(i), map.get(s.charAt(i)) + 1);
}
}
for (int j = 0; j < s.length(); j ++) {
if (map.get(s.charAt(j)) == 1) {
return j;
}
}
return -1;
}

// 第二种解法: 仅遍历26个字母,唯一的特征:第一个字符和最后一个字符的index相同
public int firstUniqCharB(String s) {
char[] originalChar = "abcdefghijklmnopqrstuvwxyz".toCharArray();
int index = s.length();
// 此处循环26次
for (char c : originalChar) {
int left = s.indexOf(c);
if (left != -1 && left == s.lastIndexOf(c)) {
index = Math.min(index, left);
}
}
if(index != s.length()) {
return index;
}
return -1;
}
赎金信(383)
/*
题目要求: 给出两个字符串,用第二个字符串中的字符拼接成为第一个字符串,每个字符只能使用一次。可以返回true,否则返回false。
说明: 你可以假设两个字符串均只含有小写字母。
实例:
canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true
*/
public boolean canConstruct(String ransomNote, String magazine) {
// 第一个字符串里面的字符能不能由第二个字符串里面的字符构成,若可以,返回true。否则返回false。
// 将26 个字母个数映射到一个26大小的数组上
int[] bucket = new int[26];
for (int i = 0; i < magazine.length(); i ++) {
bucket[magazine.charAt(i) - 'a'] ++;
}
for (int j = 0; j < ransomNote.length(); j ++) {
if (-- bucket[ransomNote.charAt(j) - 'a'] < 0) {
return false;
}
}
return true;
}

array-ask

寻找递增序列
// 给定一个未经过排序的数组,找到最长且连续的递增序列(在美团面试题中出现过-leetcode674题)
public static int findLengthOfLCIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int max = 0;
int curr = 1;
for (int i = 1; i < nums.length; i ++) {
if (nums[i] > nums[i - 1]) {
curr ++;
} else {
max = Math.max(max, curr);
curr = 1;
}
}
return Math.max(max, curr);
}

mongodb-base

安装步骤(centos7)
1.下载安装包
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.2.10.tgz
2.解压安装包
tar -zxvf mongodb-linux-x86_64-rhel62-3.2.10.tgz
3.将解压目录移动到(/usr/local/mongodb)
mv mongodb-linux-x86_64-rhel62-3.2.10 /usr/local/mongodb
4.在/usr/local/mongodb目录下创建/data/db目录和/logs目录
mkdir -p /usr/local/mongodb/data/db
mkdir -p /usr/local/mongodb/logs
5.在/usr/local/mongodb/logs目录下创建mongodb.log文件
touch mongodb.log
6.在/usr/local/mongodb/bin目录下创建mongodb的配置文件
touch mongodb.conf
7.编辑mongodb.conf文件
vi mongodb.conf
编辑内容
dbpath = /usr/local/mongodb/data/db #数据文件存放目录
logpath = /usr/local/mongodb/logs/mongodb.log #日志文件存放目录
port = 27017 #端口
fork = true #以守护程序的方式启用,即在后台运行
nohttpinterface = true
auth=true
bind_ip=0.0.0.0
8.修改环境变量
vi /etc/profile
添加的内容
export MONGODB_HOME=/usr/local/mongodb
export PATH=$PATH:$MONGODB_HOME/bin
9.编辑完成之后重启系统配置
source /etc/profile
10.在/usr/local/mongodb/bin目录下启动mongodb
./mongod -f mongodb.conf
11.关闭mongodb服务
./mongod -f ./mongodb.conf --shutdown
12.开启27017端口

13.使用shell登录到mongodb
./mongodb

14.登录到相关数据库
use test

15.创建用户
db.createUser(
{
user: "test",
pwd: "test",
roles: [{ role: "readWrite", db: "test" }]
}
)
16.修改配置,开启验证
auth=true
17.重启服务
停止服务,然后启动
18.登录
./mongodb
19.验证
auth('test', 'test')(返回1代表成功)
20.操作
db.user.find()
db.user.insert({"name":'jack',"age": 12})
基本操作->java api的crud
1. 引入依赖
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.9.1</version>
</dependency>

2.java代码
// 工具类
public class MongoUtil {
private static String baseUrl = "192.168.25.147:27017";
private static String userName = "test";
private static String passWord = "test";
private static final String url = "mongodb://"+ userName +":"+ passWord + "@"+ baseUrl + "/";

// 获取MongoDatabase
public static MongoDatabase getConnection(String dataName) {
// 获取MongoClient
com.mongodb.client.MongoClient mongoClient = MongoClients.create(url + dataName);
// 获取MongoDatabase
MongoDatabase db = mongoClient.getDatabase(dataName);
return db;
}
}

// 基本操作 -> 获取所有
public static void findAll(String databaseName, String collectionName) {
MongoDatabase database = MongoUtil.getConnection(databaseName);
MongoCollection<Document> collection = database.getCollection(collectionName);
FindIterable<Document> documents = collection.find();
print(documents);
}

// 基本操作 -> 插入
public static void insert(String databaseName, String collectionName) {
// 获取database
MongoDatabase database = MongoUtil.getConnection(databaseName);
// 获取Clooection
MongoCollection<Document> collection = database.getCollection(collectionName);
// 创建Document
Document document = new Document()
.append("name", "mick11")
.append("age", "33")
.append("address", "长沙")
.append("sex", "男");
// 插入document
collection.insertOne(document);
}


// 打印所有
public static void print(FindIterable<Document> documents) {
for (Document document : documents) {
System.out.println(document.toJson());
}
}

实际应用

list实现分布式队列
// 需要的依赖jedis fastjson
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
// 测试类
/**
* @Author long
* @Date 2019/9/8 15:00
*/
public class App {
private static final String url = "192.168.25.150";
private static final int port = 6379;

public static void main(String[] args) {
// 构建queue
String queueKey = "message_queue";
Jedis jedis = new Jedis(url, port);
// 消息转换器
MessageConvert<TaskItem> convert = new MessageConvert<>();
// 消息队列
RedisQueue queue = new RedisQueue(jedis, queueKey, convert);
// 生产者
MessageProducer producer = new MessageProducer(queue);
// 消费者
MessageConsumer consumer = new MessageConsumer(queue);
producer.start();
consumer.start();
}
}

// 消息队列
/**
* @Author long
* @Date 2019/9/8 14:34
* 分布式消息队列-list实现
*/
public class RedisQueue {

private Jedis jedis;
private String queueKey;

public RedisQueue(Jedis jedis, String queueKey, MessageConvert messageConvert) {
this.jedis = jedis;
this.queueKey = queueKey;
}

// 发送消息
public void push(String message) {
this.jedis.lpush(queueKey, message);
}

// 消费消息->可重试
public String poll(boolean isRetry) {
String message = jedis.rpop(queueKey);
if (message == null && isRetry) {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
message = jedis.rpop(queueKey);
}
return message;
}
}

/**
* @Author long
* @Date 2019/9/8 15:47
* 消息生产者
*/
public class MessageProducer extends Thread {
// 队列
private RedisQueue queue;
// 消息转换器
private static final MessageConvert<TaskItem> convert = new MessageConvert<>();

public MessageProducer(RedisQueue queue) {
this.queue = queue;
}

@Override
public void run() {
for (int i = 0; i < 10; i++) {
send("message:" + i);
try {
TimeUnit.MILLISECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

// 创建并发送消息
private void send(String message) {
// 创建一条消息
TaskItem item = new TaskItem();
item.id = UUID.randomUUID().toString();
item.msg = message;
// 转换为字符串发送
String sendMess = convert.messageToString(item);
queue.push(sendMess);
}
}

/**
* @Author long
* @Date 2019/9/8 15:48
* 消息消费者
*/
public class MessageConsumer extends Thread {
private RedisQueue queue;
public MessageConsumer(RedisQueue queue) {
this.queue = queue;
}

@Override
public void run() {
Random random = new Random();
while (true) {
try {
TimeUnit.MILLISECONDS.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
receive();
}
}

private void receive() {
Object object = queue.poll(true);
if (object != null) {
System.out.println(object);
}
}
}

/**
* @Author long
* @Date 2019/9/8 15:45
* 消息转换器
*/
public class MessageConvert<T> {
private Type taskType = new TypeReference<T>() {}.getType();

/**
* 将给定的消息转换为string类型
* @param t
* @return
*/
public String messageToString(T t) {
return JSON.toJSONString(t);
}

/**
* 将string类型的消息转换为T类型
* @param message
* @return
*/
public T stringToObject(String message) {
return JSON.parseObject(message, taskType);
}
}
zset实现延时队列
// 测试方法
private static void delayQueue() {
Jedis jedis = new Jedis(url, port);
// 延时队列
RedisDelayingQueue queue = new RedisDelayingQueue(jedis, "q-demo");
DelayMessageConsumer consumer = new DelayMessageConsumer(queue);
DelayMessageProducer producer = new DelayMessageProducer(queue);
producer.start();
consumer.start();
try {
// 首先让producer线程执行完成
producer.join();
// 主线程睡眠6秒,等待consumer将消息消费完成
Thread.sleep(6000);
consumer.interrupt();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/**
* @Author long
* @Date 2019/9/7 16:56
* 延时队列-> 通过zset实现
*/
public class RedisDelayingQueue {

private Jedis jedis;
private String queueKey;

public RedisDelayingQueue(Jedis jedis, String queueKey) {
this.jedis = jedis;
this.queueKey = queueKey;
}
// 执行消息的发送
public void delay(String msg, long score) {
jedis.zadd(queueKey, score, msg);
}

// 执行消息的处理
public void loop(MessageHandle handle) {
while (!Thread.interrupted()) {
// fixme 此处需要优化,保证操作的原子性
// 获取一条数据(score最小的那条数据)
Set values = jedis.zrangeByScore(queueKey, 0, System.currentTimeMillis(), 0, 1);
// 若是队列中没有任务,线程睡眠500毫秒
if (values.isEmpty()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
break;
}
continue;
}
String value = (String) values.iterator().next();
// 从queueKey中移除该元素
if (jedis.zrem(queueKey, value) > 0) {
// 将消息交给回调接口来处理
handle.handle(value);
}
}
}
}

/**
* @Author long
* @Date 2019/9/8 17:13
* 延时队列-生产者线程
*/
public class DelayMessageProducer extends Thread {

RedisDelayingQueue queue;

public DelayMessageProducer(RedisDelayingQueue queue) {
this.queue = queue;
}

@Override
public void run() {
Random random = new Random();
for (int i = 0; i < 10 ; i++) {
String message = "message:"+ i;
long score = random.nextInt(10);
send(message, score);
}
}


private void send(String msg, long delay) {
TaskItem task = new TaskItem();
task.id = UUID.randomUUID().toString();
task.msg = msg;
String message = JSON.toJSONString(task);
queue.delay(message, delay);
}
}

/**
* @Author long
* @Date 2019/9/8 17:11
* 延时队列消费者线程
*/
public class DelayMessageConsumer extends Thread {

RedisDelayingQueue queue;

private final static MessageHandle handle = new MessageHandle() {
@Override
public void handle(String item) {
System.out.println(item);
}
};

public DelayMessageConsumer(RedisDelayingQueue queue) {
this.queue = queue;
}

@Override
public void run() {
this.queue.loop(handle);
}
}

/**
* @Author long
* @Date 2019/9/8 17:21
* 消息处理回调接口
*/
public interface MessageHandle {

void handle(String item);
}

单链表

单链表常见操作

1.如何实现一个单链表的逆序输出->反转单链表

public static ListNode reverse(ListNode head) {
ListNode prev = null;
ListNode next = null;
while (head != null && (next = head.next) != null) {
head.next = prev;
prev = head;
head = next;
}
head.next = prev;
return head;
}

排序算法

常见的排序算法

1.插入排序

// 插入排序算法
// 时间复杂度:平均情况:o(n^2) 最好情况:o(n) 最坏情况:o(n^2)
// 空间复杂度: o(1)
// 特点: 稳定
public static void insertSort(int[] array) {
int i,j;
for (i = 1; i < array.length; i ++) {
int temp = array[i];
for (j = i - 1; j >=0 && array[j] > temp; j --) {
// 将数组中的元素后移一位
array[j + 1] = array[j];
}
array[j + 1] = temp;
}
}

2.选择排序

原理

// 选择排序算法
// 时间复杂度: 平均情况:o(n^2) 最好情况:o(n^2) 最坏情况:o(n^2)
// 空间复杂度: o(1)
// 特点: 不稳定
public static void selectSort(int[] array) {
for (int i = 0; i < array.length; i ++) {
for (int j = i + 1; j < array.length; j ++) {
if (array[i] > array[j]) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}

3.交换排序->冒泡排序

原理

冒泡排序

// 冒泡排序算法
// 时间复杂度: 平均情况:o(n^2) 最好情况:o(n) 最坏情况:o(n^2)
// 空间复杂度: o(1)
// 特点:稳定
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i ++) {
for (int j = 0; j < array.length - i - 1; j ++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}

4.交换排序->快速排序

原理

快速排序

// 快速排序算法
// 时间复杂度: 平均情况:o(nlog2^n) 最好情况:o(nlog2^n) 最坏情况:o(n^2)
// 空间复杂度: o(nlog2^n)
// 特点:不稳定
public static void fastSort(int[] array, int low, int high) {
if (low > high) {
return;
}

int i = low;
int j = fast;
int temp = array[low];
while (i < j) {
// 从右向左找到第一个小于temp的元素,保存其下标
while (array[j] > temp && i < j) {
j --;
}
// 从左向右找到第一个大于temp的元素,保存其下标
while (array[i] <= temp && i < j) {
i ++;
}
// 交换这两个元素
int swap = array[i];
array[i] = array[j];
array[j] = swap;
}
// 交换temp
array[low] = array[i];
array[i] = temp;
// 快排temp左半边
fastSort(array, low, j - 1);
// 快排temp右半边
fastSort(array, j + 1, high);
}

5.归并排序

// 归并排序算法
// 时间复杂度: 平均情况:o(nlog2^n) 最好情况:o(nlog2^n) 最坏情况:o(nlog2^n)
// 空间复杂度:o(1)
// 特点: 稳定
public static void sortMerge(int[] array) {
sort(array, 0, array.length - 1);
}

public static void sort(int array[], int L, int R) {
if (L == R) {
return;
}
int mid = (R + L) / 2;
// 排序左半部分
sort(array, L, mid);
// 排序右半部分
sort(array, mid + 1, R);
// 合并
merge(array, L, mid, R);
}
public static void merge(int[] array, int L, int mid, int R) {
int[] temp = new int[R - L + 1];
int i = 0;
int p1 = L;
int p2 = mid + 1;
// 将数组排序后放入到临时数组temp中
while (p1 <= mid && p2 <= R) {
if (array[p1] < array[p2]) {
temp[i++] = array[p1++];
} else {
temp[i++] = array[p2++];
}
}
while(p1 <= mid) {
temp[i++] = array[p1++];
}
while(p2 <= R) {
temp[i++] = array[p2++];
}
// 将临时数组中的元素移动到array中
for (int k = 0; k < temp.length; k ++) {
array[L + k] = temp[k];
}
}

jvm-command


jvm基本指令

基本指令 指令说明
iconst_1 int型常量值1进栈
bipush 将一个byte型常量值推送至栈顶
iload_1 第二个int型局部变量进栈,从0开始计数
istore_1 将栈顶int型数值存入第二个局部变量,从0开始计数