Content Table

常用排序

下面列举常用的排序算法的实现:

  • 冒泡排序
  • 插入排序
  • 选择排序
  • 快速排序
  • 归并排序
  • 堆排序

排序的时候, 需要大量的交换数组中的 2 个元素, 使用下面的函数 swap() 进行交换:

1
2
3
4
5
6
7
8
/**
* 交换数组中指定下标的 2 个元素的值
*/
public static void swap(int[] a, int indexA, int indexB) {
int temp = a[indexA];
a[indexA] = a[indexB];
a[indexB] = temp;
}

冒泡排序

原理:

  1. 比较相邻的元素,如果第一个比第二个大,就交换他们两个,把大的交换到后面。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
  5. 有 n 个元素的数组需要进行 n-1 轮
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 冒泡排序
*/
public static void bubbleSort(int[] a) {
for (int r = 1; r < a.length; ++r) { // r: round
for (int i = 0; i < a.length-r; ++i) {
// 如果 a[i] 大于 a[i+1], 则把大的交换到后面
if (a[i] > a[i+1]) {
swap(a, i, i+1);
}
}
}
}

FastJson 反序列化 Boolean

JSON 字符串反序列化为 Java 对象时, 不只是 true 和 false 能够转换为 boolean 变量的值:

  • boolean:
    • true: true
    • false: false
  • 数字:
    • 1: true
    • 0: false
    • 其他数字: false
  • 字符串:
    • “true”: true (大小写不敏感)
    • “false”: false (大小写不敏感)
    • “1”: true
    • “0”: false
    • 其他字符串抛异常
1
2
3
4
5
6
7
8
9
10
11
{ "visible": true }    // true
{ "visible": false } // false
{ "visible": 1 } // true
{ "visible": 0 } // false
{ "visible": 3 } // false
{ "visible": "true" } // true
{ "visible": "True" } // true
{ "visible": "false" } // false
{ "visible": "falsE" } // false
{ "visible": " true" } // Exception
{ "visible": "Bla" } // Exception

Vue 中使用 TinyMCE

TinyMCE 是一个功能强大的富文本编辑器:

  • 支持从 Word 中复制的文本格式
  • 拖拽修改图片的大小
  • 表格拖拽修改单元格大小
  • 提供了三种编辑模式
    • Full featured: 默认显示工具栏
    • Inline: 编辑器的到焦点时才显示工具栏
    • Distraction Free: 选中文本后才显示工具栏
  • 同一页面中可以创建多个编辑器
  • 界面美观简洁, 使用 CSS 修改样式很方便, 工具栏按钮使用 SVG 图片
  • 插件开发简单, 甚至不需要开发插件就能向工具栏插入按钮

TinyMCE 提供了 cloud 版本, 也可以下载到本地使用.

使用 TinyMCE 只需要 3 步:

  1. 引入 TinyMCE
  2. 为 TinyMCE 准备一个 DOM
  3. 基于准备好的 DOM,初始化 TinyMCE 实例

下图是使用默认参数创建的 TinyMCE 编辑器:

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>TinyMCE</title>
<!-- [1] 引入 TinyMCE -->
<script src="tinymce/tinymce.min.js"></script>
</head>

<body>
<!-- [2] 为 TinyMCE 准备一个 DOM -->
<div id="editor">道格拉斯•狗</div>

<script>
// [3] 基于准备好的 DOM,初始化 TinyMCE 实例
tinymce.init({ selector: '#editor' });
</script>
</body>

</html>

官方提供了非常丰富的文档, 请访问 https://www.tiny.cloud/docs 进行阅读, 了解 TinyMCE 更多的使用方法.

Vue 中使用 Echarts

Echarts, 一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖轻量级的矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。

Vue 项目中使用 yarn 安装 Echarts: yarn add echarts, 然后在页面中使用 Echarts 步骤如下:

  1. 为 ECharts 准备一个具备大小 (宽高) 的 DOM

  2. 引入 echarts:

    • 全部引入:
      1
      import echarts from 'echarts'; // 方便, 但是也同时引入了很多不需要的组件
    • 按需引入:
      1
      2
      import echarts from 'echarts/lib/echarts'; // 引入基本模板
      import bar from 'echarts/lib/chart/bar'; // 引入柱状图组件
  3. 基于准备好的 DOM,初始化 Echarts 实例

  4. 设置 Echarts 图表数据

入门示例

使用 Echarts 实现的柱状图:

Tio WebSocket 经验

使用 Tio 实现 WebSocket, 可以在 tio-websocket-showcase 的基础上进行修改 (据说能够达到企业级性能, 单机支持 30 万连接). 利用 Tio 提供的绑定功能实现消息群发和给指定的用户发送消息, 并且把用户对象直接存储到 ChannelContext 上, 还能省去自己管理用户的麻烦, Tio 也提供了心跳检测功能, IP 黑名单, 流量监控等, 我们只需要关注与业务层代码即可, 下面介绍一些相关的经验:

  • 应用中只有一个 GroupContext, 发送消息, 获取小组信息等

  • 一个连接对应一个 ChannelContext (ip:port), 可以使用 setAttribute() 存储业务数据, getAttribute() 获取数据

  • 绑定 (使用 Tio 进行绑定, 可参考让网络编程更轻松和有趣 t-io):

    • userid: 一个 userid 可以绑定多个 ChannelContext (实现同一个账号多个设备登录)

      1
      2
      bindUser(ChannelContext channelContext, String userid)
      SetWithLock<ChannelContext> getChannelContextsByUserid(GroupContext groupContext, String userid)
    • token: 一个 token 可以绑定多个 ChannelContext (实现同一个账号多个设备登录)

      1
      2
      bindToken(ChannelContext channelContext, String token)
      SetWithLock<ChannelContext> getChannelContextsByToken(GroupContext groupContext, String token)
    • bsId: 一个 bsId 只能绑定一个 ChannelContext (实现同一个账号只允许登录一个设备)

      1
      2
      bindBsId(ChannelContext channelContext, String bsId)
      ChannelContext getChannelContextByBsId(GroupContext groupContext, String bsId)

      下面介绍的绑定以 bsId 为例, 其他的方式参考实现即可

简单的 Mindmap

树形结构如果作为侧边栏, 使用 zTree 比较合适, 下图所示的平铺展开整棵树, 使用 zTree 估计就不适合了, 在此我们使用 jQuery 的另一个插件 Simple jQuery Mind Map Diagram Plugin - mindmap 来实现.

文档的例子使用静态的 HTML 标签准备 mindmap 需要的树形结构的数据, 实际中大多情况下树形结构的数据是存储在文件或者通过接口返回, 我们这里展示递归的方式遍历树形结构的数据创建 mindmap 需要的 HTML 标签. 因为每个节点都是 HTML 的标签, 能很方便的使用 CSS 定制样式.

Ckeditor 5 简介

新版富文本编辑器 Ckeditor 5 比 Ckeditor 4 使用更简单, 下载 zip 包后只需要里面的 ckeditor.js (连样式文件都不需要), 像下面这样 3 步就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
<meta charset="utf-8">
<title>Ckeditor Demo</title>
</head>

<body>
<div id="editor"></div> <!-- [1] -->

<script src="ckeditor.js"></script> <!-- [2] -->
<script>
ClassicEditor.create(document.querySelector('#editor')); // [3]
</script>
</body>

</html>

只是简单的使用, 默认的功能就可以了. 如果还需要更多的功能, 例如集成多种编辑模式, 使用非默认自带插件, 自定义插件等就不能使用直接下载的代码 (You can’t add a plugin to an existing build), 需要我们自己编译源码才行, 下面就一一进行介绍.

圆角 button and widget 组合

下图的控件估计很多数人会使用绘图的方式实现, 代码实现起来虽然不会很复杂, 但是要控制得到满意的效果就不太容易, 对于我来说, 能不使用继承就不使用继承 (例如给某些控件处理事件时, 能用 eventFilter 实现就尽量避免使用继承, 因为继承又要多出几个源码文件 =_=!!!). 巧妙的利用 QSS, 一个 QPushButton 和 QWidget 也能实现.

下面就来看实现的方法吧:

  1. 在 QtCreator 中如下图在一个 QWidget 中放一个 QPushButton, 右边放一个 Horizontal Spacer, 然后水平布局

  2. 使用下面的 QSS 就可以得到我们想要的效果了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #widget {
    min-height: 40px;
    max-height: 40px;
    border: 4px solid white;
    border-top-left-radius: 24px;
    border-bottom-left-radius: 24px;
    padding: 0;
    background: #AAA;
    }

    QPushButton {
    min-height: 40px;
    max-height: 40px;
    min-width: 40px;
    max-width: 40px;
    border: 4px solid white;
    border-radius: 24px;
    background: lightblue;
    }

    QPushButton:hover {
    background: orange;
    }

补充说明:

  • 高: widget 和 button 的最大最小高都设置为 40px, 这样就不会随布局的变化改变了, 圆角就不会受影响
  • 宽: button 的最大最小宽都设置为 40px, 这样就可以把按钮设置为圆形, 而不会变成圆角矩形
  • 圆角半径: 为 24px 而不是 20px, 因为圆角的计算需要把边框的宽度也计算在内, 边框的宽度是 4px, 所以圆角半径设置为 24px
  • Padding: 为了 widget 和 button 之间不留空隙, 设置 widget 的 padding 为 0
  • 边框和背景色就无需多说了, 根据自己的爱好设置即可

Gradle 管理 Scala 项目

可以使用 Gradle 来管理 Scala 项目, 和管理 Java 的项目是一样的.

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
scala
├── build.gradle
└── src
├── main
│   ├── java
│   ├── resources
│   └── scala
│   └── AppDemo.scala
└── test
├── java
├── resources
└── scala

初始文件

build.gradle 的内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
plugins {
id 'java'
id 'scala'
id 'application'
id 'com.github.johnrengelman.shadow' version '4.0.3'
}

// [1] Scala 需要的依赖
dependencies {
compile "org.scala-lang:scala-library:2.12.7"
compile "org.scala-lang:scala-compiler:2.12.7"
compile "org.scala-lang:scala-reflect:2.12.7"
}

// [2.1] 从命令行运行默认类: gradle run
// [2.2] 从命令行运行某个类: gradle run -DmainClass=Foo
ext {
project.mainClassName = System.getProperty("mainClass", "AppDemo")
}

// [3] 打包: gradle clean shadowJar [-DmainClass=Foo]
shadowJar {
mergeServiceFiles('META-INF/spring.*')
}

插件 shadowJar 用于项目打包, 更多信息请参考 https://qtdebug.com/misc-gradle-app/

AppDemo.scala 的内容为:

1
2
3
4
5
object AppDemo {
def main(args: Array[String]): Unit = {
println("Hello World")
}
}

运行打包

  • 运行: gradle run

    运行对象 AppDemo 的 main 函数, 因为 project.mainClassName 中定义了默认运行的类名为 AppDemo

  • 打包: gradle clean shadowJar

Scala 语法摘要

Scala: Scalable language, 面向对象的函数式编程语言.

类型

类型的首字母大写, 没有基本类型:

  • Boolean
  • Byte
  • Char
  • Short
  • Int
  • Long
  • Float
  • Double
  • String
  • Unit (即是 void)
  • Any
  • AnyRef
  • AnyValue

变量

常量: val greeting: String = "Hello world" (value), val π = 3.1415926

变量: var greeting: String = "Hello world" (variable)

名字: 字母, 数字, 特殊操作符如 +, -, *, /, π, θ 等, 不能以数字开头