我在 GitHub 上的开源项目

  1. java_technology_summary 🤩1  ⭐️12  🖖3  🏠http://vc2x.com Java工程师技术知识点总结 - 面试向 - 分布式 - MQ - 缓存 - 架构 - 知识体系脑图 2. quickboot Java 🤩1  ⭐️1  🖖0 基于springboot的小型快速开发框架 3. solo-blog 🤩1  ⭐️0  🖖0  🏠http://vc2x.com valarchie 的技术小屋 - Keep Thinking Keep Moving
阅读全文 »

苏世民的25条工作和生活原则

  1. 做大事和做小事的难易程度是一样的。所以要选择一个值得追求的宏伟目标,让回报与你的努力相匹配。
  1. 最优秀的高管不是天生的,而是后天磨砺的结果。他们好学不倦,永无止境。要善于研究你生活中取得巨大成功的人和组织,他们能够提供关于如何在现实世界获得成功的免费教程,可以帮助你进行自我提升。
  1. 给你敬佩的人写信或打电话,请他们提供建议或与其会面的机会。你永远不知道谁愿意跟你见面。最后你会从这些人身上学到很多重要的东西,建立你在余生都可以享用的人际关系。在生命早期结交的人,会与你缔结非同寻常的感情纽带。
  1. 人们总觉得最有意思的话题就是与自己相关的话题。所以,要善于分析他人的问题所在,并尝试提出办法来帮助他人。几乎所有的人,无论他声名多么显赫、地位多么高贵,都愿意接受新的想法。当然,前提是这些想法必须经过深思熟虑。
  1. 每个企业都是一个封闭的集成系统,内部各个组成部分性能独特却又相互关联。优秀的管理者既洞悉每个部分如何独立运行,也熟知各部分之间如何相互协作
  1. 信息是最重要的商业资产。掌握得越多,拥有的视角就越多,在竞争对手面前就越有可能发现常规模式和异常现象。所以要始终对进入企业的新鲜事物保持开放的态度,无论是新的人、新的经验,还是新的知识。
  1. 在年轻的时候,请接受能为自己提供陡峭的学习曲线和艰苦的磨炼机会的工作。最初的工作是为人生打基础的,不要为了暂时的声望而轻易地接受一份工作。
阅读全文 »

JDK8 函数式编程

函数式编程

为什么需要函数式编程?

在一般的开发过程中我们的函数式编程都是通过匿名类进行传输函数。对于一个不会复用且不易命名或者不好放进某个具体的类中的临时方法。我们都可以使用匿名函数对象来进行处理。而JDK8对开发中比较常见的几种情况提供了一系列通用的函数对象。

四个主要的函数式对象
  • Consumer : 描述消费泛型T对象且无返回值的匿名函数。
  • Supplier : 描述有返回值为泛型T对象的匿名函数。
  • Function : 描述输入泛型T,返回泛型R的匿名函数。
  • Predicate: 描述匿名的条件判断。
我们以订单以及订单中的商品举例来说明以下四种函数对象

订单类:

public class Order {
    private List<Item> itemList;
    public List<Item> getItemList() {
        return itemList;
    }
    public void setItemList(List<Item> itemList) {
        this.itemList = itemList;
    }
}

阅读全文 »

8分钟搭建Yapi小教程

废话不多说,直接安装吧~

一、安装nodejs

1.获取资源(部署nodejs尽可能选择偶数版本,因为偶数版本官方有较长的维护时间,故这次选择8.x。)

curl -sL https://rpm.nodesource.com/setup_8.x | bash -

2.安装node.js

yum install -y nodejs

3.查看版本检验是否安装成功

node -v

4.查看npm版本

npm -v
阅读全文 »

大文件小内存排序问题(阿里笔试题)

关于大文件小内存的问题很常见,今天心血来潮想试试自己写一下这个代码。

1.生成4G的数字文本文件

通过计算每行的字节大小,累加到4G的话就停止写入

 /**
 * 生成一个4G的文件
 *
 * @param file
 * @throws IOException
 */
public static void makeBigFile(File file) throws IOException {

    FileOutputStream fos = new FileOutputStream(file);

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"));
    // 随机数生成种子
    Random rand = new Random(1);

    long fileByteLength = 0;
    // BIG_FILE_SIZE为4G
    while (fileByteLength < BIG_FILE_SIZE) {

        String randomIntStr = rand.nextInt(1000000000) + "";
        bw.write(randomIntStr);
        bw.newLine();

        // 文件大小等于数字字符串的字符串字节大小加上换行符为两个字节
        fileByteLength += randomIntStr.getBytes().length + 2;

    }

    bw.close();

}

2.将大文件分割为256mb的小文件在内存中进行排序

当超过256mb文件时,即更换另一个新的文件。(第一次写,条件没设置好,结果生成了一百多万个空文件,删除花了N久)

 /**
 * 将大文件拆分为不同的小文件
 *
 * @param bigFile 4G大文件
 * @throws IOException
 */
public static void splitBigFileToSmallFile(File bigFile, String dirStr) throws IOException {

    // 提前创建目录
    File dir = new File(dirStr);

    if (!dir.exists()) {
        dir.mkdirs(); //创建目录
    }

    BufferedInputStream fis = new BufferedInputStream(new FileInputStream(bigFile));

    // 用5M的缓冲读取文本文件
    BufferedReader reader = new BufferedReader(new InputStreamReader(fis, "utf-8"), BUFFER_SIZE);

    String line = null;

    int fileNum = 1;
    long smallFileByteLength = 0;


    File smallFile = new File(dirStr + fileNum + ".txt");
    FileOutputStream fos = new FileOutputStream(smallFile);

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"));

    while ((line = reader.readLine()) != null) {

        smallFileByteLength += getRowByteLength(line);

        bw.write(line);
        bw.newLine();

        // 当文件超过固定大小的话,进行拆分
        if (smallFileByteLength > FILE_LIMIT_SIZE) {

            bw.flush();

            // 文件数递增
            fileNum++;
            // 小文件的字节大小重置为0
            smallFileByteLength = 0;

            smallFile = new File(dirStr + fileNum + ".txt");
            fos = new FileOutputStream(smallFile);
            bw = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"));

        }

        // 避免代码错误生成非常多小文件
        if (fileNum > 20) {
            break;
        }

    }


    bw.flush();
    bw.close();

}

阅读全文 »

常见算法总结 - 二叉树篇

本文总结了常见高频的关于二叉树的算法考察。

1.计算一个给定二叉树的叶子节点数目。

可以采用递归的方式进行累加

public static int calculateTreeNodeNumber(TreeNode treeNode) {

        if (treeNode == null) {
            return 0;
        }

        return calculateTreeNodeNumber(treeNode.left) + calculateTreeNodeNumber(treeNode.right) + 1;

}

2.计算二叉树的深度。

跟上题一样采用递归的方式,但需返回左右子树中较深的深度。

public static int getTreeDepth(TreeNode tree) {

        if (tree == null) {
            return 0;
        }

        int left = getTreeDepth(tree.left);
        int right = getTreeDepth(tree.right);

        return left >= right ? left + 1 : right + 1;
}

阅读全文 »

常见算法总结 - 排序篇

本文总结了常见高频的关于排序的算法考察。

1.冒泡排序

冒泡排序的思想是元素两两比较,将较大或者较小的元素往一端进行移动

 public static void bubble(int[] array) {

        for (int i = 0; i < array.length - 1; i++) {

            for (int j = 0; j + 1 < array.length - i; j++) {

                if (array[j] > array[j + 1]) {

                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;

                }

            }

        }

}

阅读全文 »

常见算法总结 - 链表篇

本文总结了常见高频的关于链表的算法考察。

1.如何找到链表的中间元素?

我们可以采用快慢指针的思想,使用步长为1的慢指针和步长为2的快指针,当快指针抵达链表末尾时,此时慢指针指向的即为中点位置。


public static LinkNode findMiddleByPointer(LinkNode node) {

    LinkNode slow = node;
    LinkNode fast = node;
    // 检测快指针是否可以安全移动
    while (fast.next != null && fast.next.next != null) {

        slow = slow.next;
        fast = fast.next.next;

    }

    return slow;

}

我们还可以采用递归的方式,当递归到最末尾的时候,我们已经能知道链表的长度,此时当递归回去的时候,判断当前递归层级等于链表长度一半的时候,即为链表的重点。

public static void findMiddleByRecursion(LinkNode node, int recursionIndex) {

        if (node.next != null) {
            findMiddleByRecursion(node.next, recursionIndex + 1);
        } else {
            middleIndex = recursionIndex % 2 == 0 ? recursionIndex / 2 : recursionIndex / 2 + 1;
        }

        if (middleIndex == recursionIndex) {
            System.out.println(node.value);
        }

        return;

    }
阅读全文 »

常见算法总结 - 数组篇

1.给定一个数值在1-100的整数数组,请找到其中缺少的数字。

找到丢失的数字 利用byte数组的1或0标记该数字是否被删除,例如byte数组下标为0的数值为1的话,代表数字1存在

public static void findMissNumber1(int[] ints) {
        // 声明一个byte数组
        byte[] isExist = new byte[100];
        for (int i = 0; i < ints.length; i++) {
            // 由于数值比下标大1, 0位置其实代表的是数字1
            isExist[ints[i] - 1] = 1;

        }
        for (int i = 0; i < isExist.length; i++) {
            if (isExist[i] == 0) {
                System.out.println("删除的数字是:" + ++i );
            }
        }
}

我们可以利用1-100的总和为5050,我们依次减掉数据内的所有值,得到的差值即为删除的值

public static void findMissNumber2(int[] ints) {
        int sum = 5050;
        for (int i = 0; i < ints.length; i++) {
            sum -= ints[i];
        }
        System.out.println("删除的数字:" + sum);
}

2.从数组中找出给定目标数字的两数组合。例如数组为{2, 3, 5, 7, 8, 9, 11, 14, 18},给定数字给17,那么数组内的3+14=17。

先利用Set存储对应的数字,然后再遍历数组,假设遍历到数字3的时候,检查set中是否存在(17-3)=14这个数字,如果存在的话,即存在这个组合。

public static void findPairNumber(int[] arrays, int target) {

        Set<Integer> existIntegers = new HashSet<>();
        for (int i = 0; i < arrays.length; i++) {
            existIntegers.add(arrays[i]);
        }
        for (int i = 0; i < arrays.length; i++) {
            if (existIntegers.contains(target - arrays[i])) {
                System.out.println("找到对应的数字组合:" + arrays[i] + "和" + (target - arrays[i]));
                // 去除掉已使用过的数字
                existIntegers.remove(arrays[i]);
            }
        }
    }

3.将数组进行反转。

只需要将数组按中间位置为对称轴进行位置交换即可

public static void reverseArray(int[] arrays) {

    for (int i = 0; i < arrays.length/2; i++) {

        int tmp = arrays[i];
        arrays[i] = arrays[arrays.length -1 - i];
        arrays[arrays.length -1 - i] = tmp;

    }

}
阅读全文 »