Silently9527

Silently9527 查看完整档案

成都编辑华北理工学院  |  软件工程 编辑成都某在线教育公司  |  JAVA 编辑 silently9527.cn 编辑
编辑

个人动态

Silently9527 发布了文章 · 3月8日

熬夜重构了IDEA插件Toolkit(整合程序员常用的工具箱),更加便捷

背景

自己在开发的过程中经常会使用一些在线的工具,比如:时间戳转日期,JSON格式化等等;考虑想把这些常用的功能都做成IDEA插件,在使用的时候就不用去网上寻找工具,在IDEA中就可以快速完成提升开发人员开发效率;

为什么要重构

最初实现了一个初级版本,大家的反馈还是比较实用,但是也收到了许多的建议和问题,比如:之前的版本tab太多,能否配置隐藏等等;加上本身这个插件集成的工具越来越来,采用Tab的方式也不太合适,所有进行了重新的设计,把所有的工具都采用命令的方式在RunAnything中运行即可,根据自己的实际情况在idea中使用需要的功能。

How to use

  1. 双击control或者点工具类图标即可弹出RunAnything窗口

  1. 在RunAnything中输入?查看 toolkit 命令

  1. 输入toolkit即可看到所有支持的命令

  1. 选择需要使用的命令,例如:SQL转elasticSearch语句、二维码生成


How to install

第一种方式:下载源码自己打包安装

第二种方式:

  1. 关注微信公众号:贝塔学java,回复:toolkit 即可获取已打包的插件,最新插件版本1.0.3
  2. 打包idea的插件安装界面 -> install plugin from disk ,选择下载的最新插件包,重启idea接口生效

支持的命令

命令说明
toolkit date日期转时间戳
toolkit timestamp时间戳转日期
toolkit jsonJSON格式化
toolkit sql2dslSQL转elasticSearch语句
toolkit url encodeURL编码
toolkit url decodeURL解码
toolkit base64 encodebase64编码
toolkit base64 decodebase64解码
toolkit phone手机号归属地
toolkit ipIP归属地
toolkit md5MD5加密
toolkit sha1SHA1加密
toolkit sha224SHA224加密
toolkit sha256SHA256加密
toolkit sha384SHA384加密
toolkit sha512SHA512加密
toolkit uuidUUID随机值
toolkit regular正则表达式
toolkit qrcode encode生成二维码(支持插入Logo)

计划中的命令

命令说明
toolkit nat内网穿透(方便微信、支付宝等后台开发)
toolkit cronCron表达式
toolkit file minifyjs/css混淆压缩
toolkit file download文件下载
toolkit qrcode decode二维码解析
欢迎小伙伴留言希望支持的命令

测试通过IDEA的版本

测试通过的版本如下,如果其他版本出现问题,欢迎给我留言

| IDE | 版本 |
| --- | --- |
| Intellij IDEA | 2019、2020 |

项目地址

Github地址: https://github.com/silently95...

Gitee地址: https://gitee.com/silently952...

觉得好用的小伙伴记得小手一抖 star 哟


点关注,不迷路

白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

完全开源的淘客项目:https://github.com/silently9527/mall-coupons-server
微信公众号:贝塔学Java
查看原文

赞 0 收藏 0 评论 0

Silently9527 发布了文章 · 2月24日

程序员常用的IDEA插件ToolSet版本更新啦

完全开源的淘客项目:https://github.com/silently9527/mall-coupons-server

微信公众号:贝塔学Java

前言

自己在开发的过程中经常会使用一些在线的工具,比如:时间戳转日期,JSON格式化等等;前几天思考了下想把这些常用的功能都做成IDEA插件,在使用的时候就不用去网上寻找工具,在IDEA中就可以快速完成提升开发人员开发效率,所以就熬夜肝了这个插件,欢迎大家都来使用。

Github地址: https://github.com/silently95...

Gitee地址: https://gitee.com/silently952...

觉得好用的小伙伴记得小手一抖 star 哟

版本更新

  • 修复了大家反馈的一些问题
  • 新增了md5加密功能
  • 新增了生成二维码,下载二维码,插入logo功能
  • 弹窗位置从右侧改到了下边

已实现功能

  • [x] SQL 转换成 ElasticSearch 查询语句
  • [x] 正则表达式
  • [x] Base64编码/解码
  • [x] JSON格式化
  • [x] URL编码/解码
  • [x] 手机号归属地
  • [x] IP地址
  • [x] 日期时间戳互转
  • [x] MD5
  • [x] 生成二维码

计划中的功能

  • [ ] Cron表达式
  • [ ] 图片base64编码
  • [ ] UUID生成器
  • [ ] 文件下载
  • [ ] js/css混淆压缩
  • [ ] 标签页支持手动设置

点关注,不迷路

白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

查看原文

赞 5 收藏 3 评论 6

Silently9527 发布了文章 · 2月22日

常见的初级排序算法,这次全搞懂

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

程序员常用的IDEA插件:https://github.com/silently9527/ToolsetIdeaPlugin

完全开源的淘客项目:https://github.com/silently9527/mall-coupons-server

微信公众号:贝塔学Java

前言

相信所有的程序员刚开始接触到的算法都会是排序算法,因为排序在对数据处理和计算有这重要的地位,排序算法往往是其他算法的基础;本文我们就先从初级排序算法开始学习算法。

排序算法的模板

在开始之前我们先定义一个排序算法通用的模板,在后面的排序算法都会实现这个模板

public interface SortTemplate {

    void sort(Comparable[] array);

    default void print(Comparable[] array) {
        for (Comparable a : array) {
            System.out.print(a + " ");
        }
    }

    default boolean less(Comparable a, Comparable b) {
        return a.compareTo(b) < 0;
    }

    default void exch(Comparable[] array, int i, int j) {
        Comparable tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
    
}
  • Comparable: 为了让我们实现的排序算法更加的通用,可以排序任意的对象,所以我们这里使用了Comparable数组
  • sort: 不同的排序算法实现的方式不一样,子类自己去实现
  • less: 定义的公用方法,如果a < b就返回true
  • exch: 定义的公用方法,交换数组中的两个对象
  • print: 打印出数据中的每个元素

选择排序

算法实现的思路:

  • 首先找到数组中的最小元素,
  • 其实将它和数组中的第一个元素进行交换,这样就排定了一个元素;
  • 再次找出剩余元素中最小的元素与数组中的第二个元素进行交换,如此反复直到所有元素都是有序的

代码实现:

public class SelectionSort implements SortTemplate {

    @Override
    public void sort(Comparable[] array) {
        int length = array.length;
        for (int i = 0; i < length; i++) {
            int min = i;
            for (int j = i + 1; j < length; j++) {
                if (less(array[j], array[min])) {
                    min = j;
                }
            }
            exch(array, i, min);
        }
    }

}

假如输入的数组是有序的,我们会发现选择排序运行的时候和未排序的时间一样长!

对于N个元素的数组,使用选择排序的时间复杂度是O(n²)

选择排序的是数据移动最少的,交换的次数与数组的大小是线性关系,N个元素的数组需要N次交换

冒泡排序

算法实现的思路:

  • 比较相邻的两个元素,如果前一个比后一个大,那么就交换两个元素的位置
  • 对每一组相邻的元素执行同样的操作,直到最后一个元素,操作完成之后就可以排定一个最大的元素
  • 如此往复,直到数组中所有的元素都有序

代码实现:

public class BubbleSort implements SortTemplate {

    @Override
    public void sort(Comparable[] array) {
        int length = array.length - 1;
        for (int i = 0; i < length; i++) {
            for (int j = 0; j < length - i; j++) {
                if (less(array[j + 1], array[j])) {
                    exch(array, j, j + 1);
                }
            }
        }
    }

}

对于N个元素的数组,使用冒泡排序的时间复杂度是O(n²)

插入排序

想象我们在玩扑克牌时,整理扑克牌都是把每一张插入到左边已经排好序的牌中适当的位置。插入排序的思路类似

算法实现的思路:

  • 初始默认第一个元素就是有序的,当前索引的位置从0开始
  • 先后移动当前索引的位置,当前索引位置左边的元素是有序的,从后往前开始扫码与当前索引位置元素进行比较
  • 当确定当前索引位置上的元素在左边有序适合的位置之后,插入到该位置上
  • 如果当确定当前索引位置上的元素大于了已排序的最后一个元素,那么当前索引位置直接往后移动
  • 如此反复,直到所有元素有序

代码实现:

public class InsertionSort implements SortTemplate {

    @Override
    public void sort(Comparable[] array) {
        int length = array.length;
        for (int i = 1; i < length; i++) {
            for (int j = i; j > 0 && less(array[j], array[j - 1]); j--) {
                exch(array, j, j - 1);
            }
        }
    }

}

从代码的实现我们可以看出,当遇到了当前索引的元素大于了左边有序数组的最后一个元素时,内层循环就直接结束了,所以所我们排序的数组中存在着部分有序,那么插入排序算法会很快。

考虑最糟糕的情况,如果输入数组是一个倒置的,那么插入排序的效率和选择排序一样,时间复杂度是O(n²)

希尔排序

对于大规模的乱序数组插入排序很慢,是因为它只交换相邻的元素,元素只能一点一点的从数组中移动到正确的位置;插入排序对于部分有序的数组排序是的效率很高;

希尔排序基于这两个特点对插入排序进行了改进;

算法实现的思路

  • 首先设置一个步长h用来分隔出子数组
  • 用插入排序将h个子数组独立排序
  • 减小h步长继续排序子数组,直到h步长为1
  • 当步长为1时就成了普通的插入排序,这样数组一定是有序的

希尔排序高效的原因,在排序之初,各个子数组都很短,子数组排序之后都是部分有序的,这两种情况都很适合插入排序。

代码实现:

public class ShellSort implements SortTemplate {

    @Override
    public void sort(Comparable[] array) {
        int gap = 1;
        int length = array.length;

        while (gap < length / 3) {
            gap = 3 * gap + 1;
        }

        while (gap >= 1) {
            for (int i = gap; i < length; i++) {
                for (int j = i; j >= gap && less(array[j], array[j - gap]); j -= gap) {
                    exch(array, j, j - gap);
                }
            }
            gap = gap / 3;
        }
    }

}

最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。

最后,写作不易,请不要白嫖我哟,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

文中所有源码已放入到了github仓库https://github.com/silently9527/JavaCore

图片来源于网络
参考书籍:算法第四版

查看原文

赞 2 收藏 1 评论 0

Silently9527 发布了文章 · 2月18日

如何检测社交网络中两个人是否是朋友关系(union-find算法)

前言

春节放假会了老家,停更了很多天,这是年后连夜肝出来的第一篇文章,先来聊聊春节放假期间发生的事,这次回家遇到了我学生时代的女神,当年她在我心目中那是

"出淤泥而不染、濯清涟而不妖"

没想到这次回家遇到了她,身体发福了,心目中女神的形象瞬间碎了,就好像达芬奇再次遇到了蒙娜丽莎

"菡萏香销翠叶残"

好了,言归正传。

有时候我们可以需要判断在大型网络中两台计算机是否相连,是否需要建立一条新的连接才能通信;或者是在社交网络中判断两个人是否是朋友关系(相连表示是朋友关系)。在这种应用中,通常我们可能需要处理数百万的对象和数亿的连接,如何能够快速的判断出是否相连呢?这就需要使用到union-find算法

概念

相连

假如输入一对整数,其中每个数字表示的是某种对象(人、地址或者计算机等等),整数对p,q理解为“p与q相连”,相连具有以下特性:

  • 自反性:p与p是相连的
  • 对称性:如果p与q相连,那么q与p相连
  • 传递性:如果p与q相连,q与r相连,那么p与r也相连
对象如何与数字关联起来,后面我们聊到一种算法符号表

等价类

假设相连是一个种等价关系,那么等价关系能够将对象划分为多个等价类,在该算法中,当且仅当两个对象相连时他们才属于同一个等价类

触点

整个网络中的某种对象称为触点

连通分量

将整数对称为连接,将等价类称作连通分量或者简称分量

动态连通性

union-find算法的目标是当程序从输入中读取了整数对p q时,如果已知的所有整数对都不能说明p q是相连的,那么将这一对整数输出,否则忽略掉这对整数;我们需要设计数据结构来保存已知的所有整数对的信息,判断出输入的整数对是否是相连的,这种问题叫做动态连通性问题。

union-find算法API定义

public interface UF {
    void union(int p, int q); //在p与q之间添加一条连接

    int find(int p); //返回p所在分量的标识符

    boolean connected(int p, int q); //判断出p与q是否存在于同一个分量中

    int count(); //统计出连通分量的数量
}

如果两个触点在不同的分量中,union操作会使两个分量归并。一开始我们有N个分量(每个触点表示一个分量),将两个分量归并之后数量减一。

抽象实现如下:

public abstract class AbstractUF implements UF {
    protected int[] id;
    protected int count;

    public AbstractUF(int N) {
        count = N;

        id = new int[N];
        for (int i = 0; i < N; i++) {
            id[i] = i;
        }
    }

    @Override
    public boolean connected(int p, int q) {
        return find(p) == find(q);
    }

    @Override
    public int count() {
        return count;
    }
}

接下来我们就主要来讨论如何实现union方法和find方法

quick-find算法

这种算法的实现思路是在同一个连通分量中所有触点在id[]中的值都是相同的,判断是否连通的connected的方法就是判断id[p]是否等于id[q]。

public class QuickFindImpl extends AbstractUF {
    public QuickFindImpl(int N) {
        super(N);
    }

    @Override
    public int find(int p) {
        return id[p];
    }

    @Override
    public void union(int p, int q) {
        int pId = find(p);
        int qId = find(q);

        if (pId == qId) { //如果相等表示p与q已经属于同一分量中
            return;
        }

        for (int i = 0; i < id.length; i++) {
            if (id[i] == pId) {
                id[i] = qId; //把分量中所有的值都统一成qId
            }
        }
        count--; //连通分量数减一
    }

}
  • 算法分析:

find()操作显然是很快的,时间复杂度O(1), 但是union的算法是无法处理大型数据的,因为每次都需要变量整个数组,那么union方法的时间复杂度是O(n)

quick-union算法

为了提高union方法的速度,我们需要考虑另外一种算法;使用同样的数据结构,只是重新定义id[]表示的意义,每个触点所对应的id[]值都是在同一分量中的另一个触点的名称

在数组初始化之后,每个节点的链接都指向自己;id[]数组用父链接的形式表示了森林,每一次union操作都会找出每个分量的根节点进行归并。

public class QuickUnionImpl extends AbstractUF {
    public QuickUnionImpl(int N) {
        super(N);
    }

    @Override
    public int find(int p) {
        //找出p所在分量的根触点
        while (p != id[p]) {
            p = id[p];
        }
        return p;
    }

    @Override
    public void union(int p, int q) {
        int pRoot = find(p); //找出q p的根触点
        int qRoot = find(q);
        if (pRoot == qRoot) { //处于同一分量不做处理
            return;
        }
        id[pRoot] = qRoot; //根节点
        count--;
    }

}
  • 算法分析:

看起来quick-union算法比quick-find算法更快,因为union不需要为每对输入遍历整个数组,
考虑最佳情况下,find方法只需要访问一次数组就可以得到根触点,那么union方法的时间复杂度O(n);
考虑到最糟糕的输入情况,如下图:

find方法需要访问数组n-1次,那么union方法的时间复杂度是O(n²)

加权quick-union算法

为了保证quick-union算法最糟糕的情况不在出现,我需要记录每一个树的大小,在进行分量归并操作时总是把小的树连接到大的树上,这种算法构造出来树的高度会远远小于未加权版本所构造的树高度。

public class WeightedQuickUnionImpl extends AbstractUF {
    private int[] sz;

    public WeightedQuickUnionImpl(int N) {
        super(N);
        sz = new int[N];
        for (int i = 0; i < N; i++) {
            sz[i] = 1;
        }
    }

    @Override
    public void union(int p, int q) {
        int pRoot = find(p); //找出q p的根触点
        int qRoot = find(q);
        if (pRoot == qRoot) { //处于同一分量不做处理
            return;
        }
        //小树合并到大树
        if (sz[pRoot] < sz[qRoot]) {
            sz[qRoot] += sz[pRoot]; 
            id[pRoot] = qRoot;
        } else {
            sz[pRoot] += sz[qRoot];
            id[qRoot] = pRoot;
        }
        count--;
    }

    @Override
    public int find(int p) {
        //找出p所在分量的根触点
        while (p != id[p]) {
            p = id[p];
        }
        return p;
    }
}
  • 算法分析:

最坏的情况下,每次union归并的树都是大小相等的,他们都包含了2的n次方个节点,高度都是n,合并之后的高度变成了n+1,由此可以得出union方法的时间复杂度是O(lgN)

总结

union-find算法只能判断出给定的两个整数是否是相连的,无法给出具体达到的路径;后期我们聊到图算法可以给出具体的路径

算法union()find()
quick-find算法N1
quick-union算法树的高度树的高度
加权quick-union算法lgNlgN

最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。

最后,写作不易,请不要白嫖我哟,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

文中所有源码已放入到了github仓库https://github.com/silently9527/JavaCore

参考书籍:算法第四版

程序员常用的IDEA插件:https://github.com/silently9527/ToolsetIdeaPlugin

完全开源的淘客项目:https://github.com/silently9527/mall-coupons-server

微信公众号:贝塔学Java

查看原文

赞 12 收藏 7 评论 0

Silently9527 发布了文章 · 2月8日

面试的季节到了,老哥确定不来复习下数据结构吗

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

微信公众号:贝塔学Java

前言

在上一次《面试篇》Http协议中,面试官原本想的是http问的差不多了,想要继续问我JAVA相关的一些问题,结果我突然觉得嗓子不舒服咳嗽了几声,(在这个敏感的时候)吓退了面试官,面试官带起口罩后就说面试先暂时到这里,下次再聊;两周之后我又收到了HR的电话;

HR:感冒好了吗?上次面试的结果不错,你什么时候有时间来我们公司二面呢?

我:随时准备着

到公司后,我依然被带到了那个小黑屋,等待着面试官的到来。没想等来的是一位美女小姐姐。

我:人美声甜的小姐姐,你是本次的面试官?(我窃喜中)

美女面试官:是的,上次面试你的面试官开会去了,这次面试我来和你聊聊

面试官:看你这么会说话,让我先来帮你开个胃,说说二分查找吧

我:(果然是开胃啊,这位小姐姐竟然如此善良)

我:二分查找法是在一个有序的数组中查到一个值,如果存在就返回在数组中的索引,否则就返回-1;算法维护了两个变量lo(最小)和hi(最大),每次查找都与中间值(mid)进行比较,如果等于就返回mid,大于就查询右半边的数组,小于就查询左半边数组。二分查找法之所以快是因为每次一次查询都会排除掉一半的值。

No BB, show you the code(不废话,直接看代码)
public class BinarySearch {

    /**
     * 二分查找
     * @param key
     * @param arr
     * @return 存在返回对应的下标,不存在返回 -1
     */
    public static int search(int key, int[] arr) {
        int lo = 0, hi = arr.length - 1;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (key > arr[mid]) {
                lo = mid + 1;
            } else if (key < arr[mid]) {
                hi = mid - 1;
            } else {
                return mid;
            }
        }
        return -1;
    }

}

对于一个包含n个元素的列表,用二分查找法最多需要log2n(前面的2是底数)次就可以判断出元素是否存在;所以二分查找法的时间复杂度是O(log n)

面试官:说说使用数组如何实现栈?

我:小姐姐,栈的特点就是后进先出,使用数组和链表都可以实现栈的功能,首先看下栈的定义:

public interface Stack<T> extends Iterable {
    void push(T item); //向栈添加元素
    T pop(); //从栈中弹出
    boolean isEmpty();
    int size(); //返回元素的个数
}



栈在使用的时候有可能也会遍历全部的元素,所以继承了Iterable,使用数组实现栈的完整代码:

public class FixCapacityArrayStack<T> implements Stack<T> {
    private T[] arr;
    private int size;

    public FixCapacityArrayStack(int capacity) {
        this.arr = (T[]) new Object[capacity]; //初始化数组大小
    }

    @Override
    public void push(T item) {
        this.arr[size++] = item;
    }

    @Override
    public T pop() {
        return this.arr[--size];
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            int index;

            @Override
            public boolean hasNext() {
                return index < size;
            }

            @Override
            public T next() {
                return arr[index++];
            }
        };
    }
}



面试官:你刚才实现的栈是定容的,那如何实现动态调整栈的大小

我:(已猜到你会问这个问题了,刚才故意没说动态调整大小;经过多年的面试经验总结,最和谐的面试过程就是与面试官你推我挡,一上来就说出了最优解,如何体现面试官的优越感?)

我:要实现动态的调整大小,首先需要在提供一个 resize 的方法,把数组扩容到指定的大小并拷贝原来的数据到新的数组中;

private void resize(int newCapacity) {
    Object[] tmp = new Object[newCapacity];
    for (int index = 0; index < size; index++) {
        tmp[index] = arr[index];
    }
    this.arr = (T[]) tmp;
}


需要push方法中检查当前的size是否已经达到了数组的最大容量,如果是,就把数组扩容2倍

@Override
public void push(T item) {
    if (this.arr.length == size) {
        this.resize(2 * this.arr.length);
    }
    this.arr[size++] = item;
}


pop方法中需要检查当前占用的空间是否是数组的四分之一,如果是,就需要把数据的容量缩小到原来的一半

@Override
public T pop() {
    T item = this.arr[--size];
    this.arr[size] = null;  //避免游离对象,让垃圾回收器回收无用对象
    if (size > 0 && size == this.arr.length / 4) {
        this.resize(this.arr.length / 2);
    }
    return item;
}



面试官:刚才你提到了链表,那么使用链表如何实现栈

我:(这就是你推我挡的结果,和小姐姐的互动很和谐)

我:使用链表,首先需要先定义个Node,单向的链表包含了值和下一个节点的引用

public class Node<T> {
    private T item;
    private Node next;
}


采用链表实现的栈相对于数组实现还较为简单一些,不需要考虑扩容的问题。

public class LinkedListStack<T> implements Stack<T> {
    private Node<T> first;
    private int size;

    @Override
    public void push(T item) {//先栈顶添加元素
        this.first = new Node<>(item, first);
        size++;
    }

    @Override
    public T pop() { //从栈顶删除元素
        T item = first.getItem();
        size--;
        first = first.getNext();
        return item;
    }

    @Override
    public boolean isEmpty() {
        return first == null;
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            private Node<T> current = first;

            @Override
            public boolean hasNext() {
                return current != null;
            }

            @Override
            public T next() {
                T item = current.getItem();
                current = current.getNext();
                return item;
            }
        };
    }
}


面试官:使用链表如何实现先进先出队列

我:与栈的实现过程类似,首先需要定义出队列

public interface Queue<T> extends Iterable {
    void enqueue(T item); //入队列

    T dequeue(); //出队列

    int size();

    boolean isEmpty();
}


使用链表实现队列需要维护两个变量first、last;first表示的是队列的头结点,last表示队列的尾结点;当入队列时enqueue向尾部结点添加元素,当出队列时dequeue从头结点删除元素。

public class LinkedListQueue<T> implements Queue<T> {
    private Node<T> first;
    private Node<T> last;
    private int size;

    @Override
    public void enqueue(T item) {
        Node<T> node = new Node<>(item, null);
        if (isEmpty()) {
            first = node; //当队列为空,first和last指向同一个元素
        } else {
            last.setNext(node);
        }
        last = node;
        size++;
    }

    @Override
    public T dequeue() {
        T item = first.getItem();
        first = first.getNext();
        if (isEmpty()) { //当队列为空时,需要把last设置为null
            last = null;
        }
        size--;
        return item;
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public boolean isEmpty() {
        return first == null;  //首节点为空
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            private Node<T> current = first;

            @Override
            public boolean hasNext() {
                return current != null;
            }

            @Override
            public T next() {
                T item = current.getItem();
                current = current.getNext();
                return item;
            }
        };
    }
}



面试官:胃开的差不多了,来聊一点算法吧;你来设计一个算法对算术表示式求值,比如:( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )

我:(昨天晚上熬夜看算法没白辛苦啊,刚好看到了这个解法。)

我:(挠挠头),这个问题有点麻烦,我需要思考一会。(这样显得我是没有提前准备的,属于临场发挥)

我:定义两个栈,一个用于保存运算符,一个用户保存操作数;具体的操作过程如下:

  • 忽略左边括号
  • 遇到数字就压入操作数栈
  • 遇到符号就压入符号栈
  • 遇到右括号,弹出一个运算符,弹出所需要的操作数,将计算的结果再次压入到操作数栈

public static int calculate(String expression) {
    Stack<String> operate = new LinkedListStack<>();
    Stack<Integer> numbers = new LinkedListStack<>();

    String[] split = expression.split(" ");
    for (String str : split) {
        if ("(".equals(str)) {
        } else if ("+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str)) {
            operate.push(str);
        } else if (")".equals(str)) {
            String op = operate.pop();
            int resut = 0;
            if ("+".equals(op)) {
                resut = numbers.pop() + numbers.pop();
            } else if ("-".equals(op)) {
                resut = numbers.pop() - numbers.pop();
            } else if ("*".equals(op)) {
                resut = numbers.pop() * numbers.pop();
            } else if ("/".equals(op)) {
                resut = numbers.pop() / numbers.pop();
            }
            numbers.push(resut);
        } else {
            numbers.push(Integer.valueOf(str));
        }
    }
    return numbers.pop();
}


面试官:一个int类型的数组,其中存在三个数字相加等于0,你来设计个算法帮我统计出有多少组这样的数字

我:这个简单,请看代码:

public static int count1(int[] arr) {
    int length = arr.length;
    int count = 0;
    for (int i = 0; i < length; i++) {
        for (int j = i + 1; j < length; j++) {
            for (int k = j + 1; k < length; k++) {
                if (arr[i] + arr[j] + arr[k] == 0) {
                    count++;
                }
            }
        }
    }
    return count;
}


面试官:假如这个数组有100万的int值,你这个算法得运行到什么时候

我:(对的哦,这个算法的时间复杂度是O(n³),在遇到数据量较大时效率极低)

(经过大脑快速思考后)

我:这个算法确实有问题,我大意了,没有考虑到大量数据的情况;用这个算法会浪费小姐姐的大好青春,所以刚才我思考了下,对这个算法进行改进一下;

首先把3-sum简化成2-sum

2-sum中,一个数a[i]要与另一个数相加等于0;有两种方法:

第一种:与3-sum实现类似使用两层循环,时间复杂度是O(n²)

第二种:只需要找出数组中是否有-a[i],查找的算法使用二分查找法

public static int count2(int[] arr) {
    Arrays.sort(arr); //首先排序
    int length = arr.length;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (BinarySearch.search(-arr[i], arr) > i) {
            count++;
        }
    }
    return count;
}


二分查找法的时间复杂度是O(log n), 实现2-sum的算法多了一层循环,所以时间复杂度O(nlog n)

对待3-sum也是用类似的方法,直接上机撸代码:

public static int count3(int[] arr) {
    Arrays.sort(arr);
    int length = arr.length;
    int count = 0;
    for (int i = 0; i < length; i++) {
        for (int j = i + 1; j < length; j++) {
            if (BinarySearch.search(-arr[i]-arr[j], arr) > j) {
                count++;
            }
        }
    }
    return count;
}


我:小姐姐,这个算法改进之后的时间复杂度是O(n²log n),我已经尽力了,只能这么快了。(面试官露出迷人的微笑)

面试官:假如你是微信的开发人员,随便给你两个用户,如何判断这两个用户是否连通的。何为连通?A是B的好友,B是C的好友,那么A与C就是连通的

我:(这小姐姐的问题是越来越难了)

我:美丽的面试官,今天烧脑严重,我可以趴下休息一会吗?(其实是没想到好的解法,拖延时间战术)

面试官:可以,那你先休息10分钟。

面试未完,待续

最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。

最后,写作不易,请不要白嫖我哟,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

文中所有源码已放入到了github仓库https://github.com/silently9527/JavaCore

参考书籍:算法第四版

程序员常用的IDEA插件:https://github.com/silently9527/ToolsetIdeaPlugin

完全开源的淘客项目:https://github.com/silently9527/mall-coupons-server

查看原文

赞 4 收藏 3 评论 0

Silently9527 发布了文章 · 2月3日

熬夜肝了个IDEA插件整合程序员常用的工具,总有你能用上的

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

微信公众号:贝塔学Java

前言

自己在开发的过程中经常会使用一些在线的工具,比如:时间戳转日期,JSON格式化等等;前几天思考了下想把这些常用的功能都做成IDEA插件,在使用的时候就不用去网上寻找工具,在IDEA中就可以快速完成提升开发人员开发效率,所以就熬夜肝了这个插件,欢迎大家都来使用。

Github地址: https://github.com/silently95...

Gitee地址: https://gitee.com/silently952...

觉得好用的小伙伴记得小手一抖 star 哟

已实现功能

  • [x] SQL 转换成 ElasticSearch 查询语句
  • [x] 正则表达式
  • [x] Base64编码/解码
  • [x] JSON格式化
  • [x] URL编码/解码
  • [x] 手机号归属地
  • [x] IP地址
  • [x] 日期时间戳互转

计划中的功能

  • [ ] Cron表达式
  • [ ] MD5
  • [ ] 图片base64编码
  • [ ] 文件下载
  • [ ] js/css混淆压缩

插件安装步骤

  1. 从release中下载最新版本的代码
  2. 在IDEA中通过本地安装插件

功能介绍:

SQL转换成ElasticSearch查询语句

手写ElasticSearch的查询语句,语法记不住的可以使用下这个工具,常用的都能正常转换,如果遇到复杂的可能会转换出错,需要在再转换的结果中自行修改

Base64编码/解码

JSON格式化

IP地址

手机号归属地

URL编码/解码

日期时间戳互转

正则表达式

提供了常用的正则表达式匹配,当然自己也可以自己写表达式


写到最后(点关注,不迷路)

白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

欢迎关注公众号:贝塔学JAVA

查看原文

赞 8 收藏 4 评论 5

Silently9527 发布了文章 · 2月2日

精美的淘客项目完全开源啦,确定不来围观吗

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

项目介绍

Mall-Coupons是一个从前端到后端完全开源的淘宝客项目,当初学习完uniapp之后想做一个实战项目,所以才研发了这个项目。由于本人平时主要从事后端研发,界面样式非我所长,所以大家觉得界面效果不好的可以自己修改。目前项目已经支持打包成App、微信小程序、QQ小程序、Web站点;理论上其他小程序也支持,可能需要微调

Github地址:

Gitee地址:

效果预览

功能列表

  • 推荐穿衣搭配
  • 搭配筛选
  • 搭配详情
  • 相关搭配推荐
  • 用户点赞
  • 商品分类
  • 分类查询商品列表
  • 首页轮播
  • APP、Web支持唤醒淘宝
  • 9.9包邮
  • 疯抢排行榜
  • 首页优质商品推荐
  • 商品、优惠券搜索
  • 商品详情
  • 相似商品推荐
  • 商品收藏、收藏夹
  • 口令购买、领券购买
  • 用户登录、微信登录、QQ登录、手机验证码登录
  • 用户新手教程

技术选型

后端技术

技术备注地址
SpringBoot容器+MVC框架https://spring.io/projects/spring-boot
MyBatisORM框架http://www.mybatis.org/mybatis-3/zh/index.html
SpringSecurity认证和授权框架https://spring.io/projects/spring-security
SpringSocialOAuth2认证框架https://github.com/spring-projects/spring-social
Redis分布式缓存https://redis.io/
Druid数据库连接池https://github.com/alibaba/druid
Lombok简化对象封装工具https://github.com/rzwitserloot/lombok
FastjsonJSON工具https://github.com/alibaba/fa...
spring-data-mybatis封装Mybatis实现JPA部分功能https://github.com/easybest/spring-data-mybatis

前端技术

技术备注地址
Vue前端框架https://vuejs.org/
UniApp一个使用 Vue.js 开发所有前端应用的框架https://uniapp.dcloud.io/
Vuex全局状态管理框架https://vuex.vuejs.org/
colorui样式库https://github.com/weilanwl/ColorUI

开发环境

工具版本号下载
JDK1.8https://www.oracle.com/techne...
Mysql5.7https://www.mysql.com/
Redis5.0https://redis.io/download
Nginx1.10http://nginx.org/en/download....

部署文档

关注微信公众号:贝塔学JAVA ;回复文档获取部署文档

有任何部署疑问,欢迎给我留言

我的技术博客

https://silently9527.cn/


写到最后(点关注,不迷路)

白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

查看原文

赞 9 收藏 7 评论 0

Silently9527 发布了文章 · 1月27日

面试官常问的垃圾回收器,这次全搞懂

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

前言

前几天写了一篇《JVM性能调优实战:让你的IntelliJ Idea纵享丝滑》,其中有对GC垃圾回收器的选择尝试,本篇我们就来详细的看看JVM中常见的垃圾回收器有哪些以及每个垃圾回收器的特点,这也是面试的时候经常被问的内容

JVM堆内存概览

在聊垃圾回收器之前,我们先来看看JVM堆内存的区域划分是怎么样的,看下图

  • 因为虚拟机使用的垃圾回收算法是分代收集算法,所以堆内存被分为了新生代和老年代
  • 新生代使用的垃圾回收算法是复制算法,所以新生代又被分为了 Eden 和Survivor;空间大小比例默认为8:2
  • Survivor又被分为了S0、S1,这两个的空间大小比例为1:1

内存分配以及垃圾回收

  1. 对象优先在Eden区进行分配,如果Eden区满了之后会触发一次Minor GC
  2. Minor GC之后从Eden存活下来的对象将会被移动到S0区域,当S0内存满了之后又会被触发一次Minor GC,S0区存活下来的对象会被移动到S1区,S0区空闲;S1满了之后在Minor GC,存活下来的再次移动到S0区,S1区空闲,这样反反复复GC,每GC一次,对象的年龄就涨一岁,默认达到15岁之后就会进入老年代,对于晋身到老年代的年龄阈值可以通过参数 -XX:MaxTenuringThreshold设置
  3. 在Minor GC之后需要的发送晋身到老年代的对象没有空间安置,那么就会触发Full GC (这步非绝对,视垃圾回收器决定)
Minor GC和Full GC的区别:Minor GC是指发生在新生代的垃圾收集行为,由于对象优先在Eden区分配,并且很多对象都是朝生夕死,所以触发的频率相对较高;由于采用的复制算法,所以一般回收速度非常快。Full GC是指发生在老年代的垃圾收集行为,Full GC的速度一般会比Minor GC慢10倍以上;所以不能让JVM频繁的发生Full GC

为了能够更好的适应不同程序的内存情况,JVM也不一定要求必须达到年龄15岁才能晋身到老年代,如果在Survivor区中相同年龄的所有对象大小总和大于Survivor区空间的一半,年龄大于或者等于这个年龄的对象将会直接进入到老年代

Full GC触发条件

  • 代码中调用System.gc()
  • 老年代空间不足/满了
  • 持久区空间不足/满了
注意:大对象会直接在老年代分配内存,可以通过参数-XX:PretenureSizeThreshold控制对象的大小,通常遇到的大对象是很长的字符串或者数组,如果分配了一大群大对象只是临时使用,生命很短暂,那么就会频繁的发生Full GC,但是此时的新生代的空间还有空闲;写代码的时候,这种情况应该避免,特别是在创建数组的时候要当心

空间担保

在新生代发生Minor GC的时候,JVM会先检查老年代中可分配的连续空间是否大于新生代所有对象的总和,如果大于,那么本次Minor GC就可以安全的执行;如果不大于,那么JVM会先去检查参数HandlePromotionFailure设置值是否允许空间担保失败,如果允许,JVM会继续检查老年代可分配的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,尽管这次Minor GC是有风险的,JVM也会尝试一次Minor GC;如果不允许担保失败,那么JVM直接进行Full GC

虽然担保有可能会失败,导致饶一圈才能进行GC,但是还是建议把这个参数打开,可以避免JVM频繁的Full GC

垃圾回收器概览

从上图可以看出:

  • 新生代可以使用的垃圾回收器:Serial、ParNew、Parallel Scavenge
  • 老年代可以适用的垃圾回收器:CMS、Serial Old、Parallel Old
  • G1回收器适用于新生代和老年代
  • 相互之间有连线的表示可以配合使用
CMS和Serial Old同为老年代回收器,为何相互会有连线呢?

Serial收集器

这是个单线程收集器,发展历史最悠久的收集器,当它在进行垃圾收集工作的时候,其他线程都必须暂停直到垃圾收集结束(Stop The World)。

虽然Serial收集器存在Stop The World的问题,但是在并行能力较弱的单CPU环境下往往表现优于其他收集器;因为它简单而高效,没有多余的线程交互开销;Serial对于运行在Client模式下的虚拟机来说是个很好的选择

使用-XX:+UseSerialGC参数可以设置新生代使用这个Serial收集器

ParNew收集器

ParNew收集器是Serial收集器的多线程版本;除了使用了多线程进行垃圾收集以外,其他的都和Serial一致;它默认开始的线程数与CPU的核数相同,可以通过参数-XX:ParallelGCThreads来设置线程数。

从上面的图可以看出,能够与CMS配合使用的收集器,除了Serial以外,就只剩下ParNew,所以ParNew通常是运行在Server模式下的首选新生代垃圾收集器

使用-XX:+UseParNewGC参数可以设置新生代使用这个并行回收器

Parallel Scavenge收集器

Parallel Scavenge收集器依然是个采用复制算法的多线程新生代收集器,它与其他的收集器的不同之处在于它主要关心的是吞吐量,而其他的收集器关注的是尽可能的减少用户线程的等待时间(缩短Stop The World的时间)。吞吐量=用户线程执行时间/(用户线程执行时间+垃圾收集时间),虚拟机总共运行100分钟,其中垃圾收集花费时间1分钟,那么吞吐量就是 99%

停顿时间越短适合需要和用户进行交互的程序,良好的响应能够提升用户的体验。而高效的吞吐量可以充分的利用CPU时间,尽快的完成计算任务,所以Parallel Scavenge收集器适用于后台计算型任务程序。

-XX:MaxGCPauseMillis可以控制垃圾收集的最大暂停时间,需要注意不要以为把这个时间设置的很小就可以减少垃圾收集暂用的时间,这可能会导致发生频繁的GC,反而降低了吞吐量

-XX:GCTimeRatio设置吞吐量大小,参数是取值范围0-100的整数,也就是垃圾收集占用的时间,默认是99,那么垃圾收集占用的最大时间 1%

-XX:+UseAdaptiveSizePolicy 如果打开这个参数,就不需要用户手动的控制新生代大小,晋升老年代年龄等参数,JVM会开启GC自适应调节策略

Serial Old收集器

Serial Old收集器也是个单线程收集器,适用于老年代,使用的是标记-整理算法,可以配合Serial收集器在Client模式下使用。

它可以作为CMS收集器的后备预案,如果CMS出现Concurrent Mode Failure,则SerialOld将作为后备收集器。(后面CMS详细说明)

Parallel Old收集器

Parallel Old收集器可以配合Parallel Scavenge收集器一起使用达到“吞吐量优先”,它主要是针对老年代的收集器,使用的是标记-整理算法。在注重吞吐量的任务中可以优先考虑使用这个组合

-XX:+UseParallelOldGc设置老年代使用该回收器。

XX:+ParallelGCThreads设置垃圾收集时的线程数量。

CMS收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器,在互联网网站、B/S架构的中常用的收集器就是CMS,因为系统停顿的时间最短,给用户带来较好的体验。

-XX:+UseConcMarkSweepGC设置老年代使用该回收器。

-XX:ConcGCThreads设置并发线程数量。

CMS采用的是标记-清除算法,主要分为了4个步骤:

  • 初始化标记
  • 并发标记
  • 重新标记
  • 并发清除

初始化标记和重新标记这两个步骤依然会发生Stop The World,初始化标记只是标记GC Root能够直接关联到的对象,速度较快,并发标记能够和用户线程并发执行;重新标记是为了修正在并发标记的过程中用户线程产生的垃圾,这个时间比初始化标记稍长,比并发标记短很多。整个过程请看下图

优点

  • CMS是一款优秀的收集器,它的主要优点:并发收集、低停顿,因此CMS收集器也被称为并发低停顿收集器(Concurrent Low Pause Collector)。

缺点

  • CMS收集器对CPU资源非常敏感。 在并发阶段,它虽然不会导致用户线程停顿,但会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU不足4个时(比如2个),CMS对用户程序的影响就可能变得很大,如果本来CPU负载就比较大,还要分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了50%,其实也让人无法接受。
  • 无法处理浮动垃圾。 由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生。这一部分垃圾出现在标记过程之后,CMS无法再当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就被称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,回收阀值可以通过参数-XX:CMSInitiatingoccupancyFraction来设置;如果回收阀值设置的太大,在CMS运行期间如果分配大的对象找不到足够的空间就会出现“Concurrent Mode Failure”失败,这时候会临时启动SerialOld GC来重新进行老年代的收集,这样的话停顿的时间就会加长。
  • 标记-清除算法导致的空间碎片 CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象。为了解决这个问题CMS提供了一个参数-XX:+UseCMSCompactAtFullCollecion,如果启用,在Full GC的时候开启内存碎片整理合并过程,由于内存碎片整理的过程无法并行执行,所以停顿的时间会加长。考虑到每次FullGC都要进行内存碎片合并不是很合适,所以CMS又提供了另一个参数-XX:CMSFullGCsBeforeCompaction来控制执行多少次不带碎片整理的FullGC之后,来一次带碎片整理GC

G1收集器

G1是一款面向服务端应用的垃圾回收器。

  • 并行与并发:与CMS类似,充分里用多核CPU的优势,G1仍然可以不暂停用户线程执行垃圾收集工作
  • 分代收集:分代的概念依然在G1保留,当时它不需要和其他垃圾收集器配合使用,可以独立管理整个堆内存
  • 空间的整合:G1整体上采用的是标记-整理算法,从局部(Region)采用的是复制算法,这两种算法都意味着G1不需要进行内存碎片整理
  • 可预测的停顿:能够让用户指定在时间片段内,消耗在垃圾收集的时间不超过多长时间。

Region

虽然在G1中依然保留了新生代和老年代的概念,但是采用的是一种完全不同的方式来组织堆内存,它把整个堆内存分割成了很多大小相同的区域(Region),并且新生代和老年代在物理上也不是连续的内存区域,请看下图:

每个Region被标记了E、S、O和H,其中H是以往算法中没有的,它代表Humongous,这表示这些Region存储的是巨型对象,当新建对象大小超过Region大小一半时,直接在新的一个或多个连续Region中分配,并标记为H。Region区域的内存大小可以通过-XX:G1HeapRegionSize参数指定,大小区间只能是2的幂次方,如:1M、2M、4M、8M

G1的GC模式

  • 新生代GC:与其他新生代收集器类似,对象优先在eden region分配,如果eden region内存不足就会触发新生代的GC,把存活的对象安置在survivor region,或者晋升到old region
  • 混合GC:当越来越多的对象晋升到了old region,当老年代的内存使用率达到某个阈值就会触发混合GC,可以通过参数-XX:InitiatingHeapOccupancyPercent设置阈值百分比,此参数与CMS中-XX:CMSInitiatingoccupancyFraction的功能类似;混合GC会回收新生代和部分老年代内存,注意是部分老年代而不是全部老年代;G1会跟踪每个Region中的垃圾回收价值,在用户指定的垃圾收集时间内优先回收价值最大的region
  • Full GC:如果对象内存分配速度过快,混合GC还未回收完成,导致老年代被填满,就会触发一次full gc,G1的full gc算法就是单线程执行的serial old gc,此过程与CMS类似,会导致异常长时间的暂停时间,尽可能的避免full gc.
    • -

写到最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。

最后,请朋友们不要白嫖我哟,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

    • -
我已经从零开始手写了简易版springmvc,以及编写了详细的说明文档,希望能够帮助伙伴们深入理解springmvc核心原理,有需要的朋友欢迎关注公众号:贝塔学JAVA ,回复源码即可

image

查看原文

赞 6 收藏 5 评论 0

Silently9527 发布了文章 · 1月25日

吐血整理:推荐几款顶级好用的IDEA插件

本文已被Github仓库收录 https://github.com/silently9527/JavaCore

微信公众号:贝塔学Java

前言

“工欲善其事必先利其器” 在实际的开发过程中,灵活的使用好开发工具,将让我们的工作事半功倍。今天给大家推荐几款好用的IDEA插件,写代码也可以“飞起来”

美化插件

Material Theme UI

相亲第一眼也得看眼缘,所以今天推荐的第一款是主题插件,可以让你的idea图标、配置搭配很到位,也可以切换不用的颜色,默认提供了很多的主题供选择,每一种都是狂拽酷炫;当前端小姐姐或者测试小姐姐看到了你这么炫酷的界面,她肯定会觉得原来男孩子也会这么精致呀,形象陡然上升~

就问你,这么绚丽多彩的颜色,哪个小姐姐不为你着迷~

Extra Icons

这也是一款美化插件,为一些文件类型提供官方没有的图标

Background Image Plus

设置idea背景图片的插件,不但可以设置固体的图片,还可以设置一段时间后随机变化背景图片,以及设置图片的透明度等等;接下来我设置一张女神的照片,看着女神照片撸代码,整天心情美滋滋

实用插件

Translation

像我这样英文很菜的人来说,这款插件就是神器,在看各种框架源码的时候十分有用; 选择右键就可以翻译,对于方法或者类上面的注释,只要按下F1就自动被翻译成中文

Maven Helper

依赖包冲突的问题,我相信大家都遇到过,一旦出现了冲突,启动或运行过程总是会出一些莫名其妙的错误,查找冲突过程十分痛苦,但如果你安装了这个插件,那这些都不是事,分分钟搞定

Code Glance

Sublime Text右侧的预览区相信很多人都用过吧, 此插件就实现了代码编辑区迷你缩放功能, 达到代码全局预览

MyBatis Log Plugin

Mybaits在运行的时候会把SQL打印出来,但是打印的是带占位符的SQL,如果遇到SQL复杂的话,那么要手动拼接出这个SQL还是比较麻烦的,好在这个插件帮我们搞定

菜单栏 -> Tools -> MyBatis Log Plugin

Free Mybatis plugin

可以在Mybatis的Mapper接口和xml文件之间很方便的来回切换,像是查看接口实现类一样简单,无需到xml中去搜索。

Lombok

神器级别的插件,可以让实体类更加简化,不在需要写getter/setter方法,通过注解就可以实现builder模式以及链式调用;在充血模型中可以不需要在和getter/setter方法混在一起

项目还需要添加maven依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
</dependency>

Key promoter X

回想刚开始从eclipse切换到idea那段时间实在是很痛苦,就是因为快捷键不熟悉,熟悉开发工具的快捷键能够很好的提高我们的开发效率,这款工具的目的就是为了帮助用户记住快捷键,操作窗口之后就会在右下角给出快捷键的提示,提醒多了自然你就记住了。

Grep Console

在开发的过程中,idea的控制台通常会打印出一大推的日志,想要快速找到自己关心的日志比较困难,通过这个插件可以给不同级别的日志设置不同的展示样式,帮助快速定位日志


写到最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。

最后,白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

原创不易 转载请注明出处:https://silently9527.cn/archives/104

我已经从零开始手写了简易版springmvc,以及编写了详细的说明文档,希望能够和伙伴们深入理解springmvc核心原理,有需要的朋友欢迎关注公众号:贝塔学JAVA ,回复源码即可


公众号:贝塔学JAVA

查看原文

赞 7 收藏 5 评论 0

Silently9527 关注了用户 · 1月22日

SegmentFault @segmentfault

SegmentFault 社区管理媛 - 思否小姐姐

纯粹的技术社区离不开所有开发者的支持和努力 biubiu

更多技术内容与动态欢迎关注 @SegmentFault 官方微博与微信公众号!

点击添加思否小姐姐个人微信号

关注 84102

认证与成就

  • 获得 86 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 5 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-11-13
个人主页被 5.2k 人浏览