【1】背景
如下图所示,这里有一个 15 × 15 的棋盘,如果现在要让你通过编码的方式,让你将这盘棋局保存起来,你会怎么做呢?
面对行列数据的保存,我相信大多人第一时间都会想到用二维数组进行保存。

【2】普通数组保存棋盘数据
比如,我们可以将棋盘进行抽象化,用一个 15 × 15 的二维数组来表示,然后用 0 表示空点,用 1 表示白子,用 2 表示黑子,于是就可以抽象为如下模样。

于是,我们可以通过如下代码,将数据保存到二维数组中。
/ * 将棋盘数据保存为二维数组 */ public static int[][] saveChess() { // 初始化棋盘(大小为 15 * 15),int默认为0(即空点) int[][] arr = new int[15][15]; // 保存白子(用1表示) arr[5][5] = 1; arr[7][5] = 1; arr[6][7] = 1; // 保存黑子(用2表示) arr[6][6] = 2; arr[7][6] = 2; arr[8][6] = 2; arr[7][7] = 2; return arr; }
【3】普通数组的不足之处
上面,我们通过了一个最普通的方法,将棋盘数据保存在了一个二维数组中,整个数组我们用了 15 × 15(共 225)个点来保存数据,其中有 218 个点都是空的。实际来说,我们只需要将有价值的黑白子保存起来即可,因为只要我们知道黑白子数据,以及棋盘的大小,那么这 218 个空点是可以不用进行保存的。
把这样没有价值的数据起来,不但会浪费存储空间的大小,如果写入到磁盘中,还会增大 IO 的读写量,影响性能,这就是用普通二维数组来表示棋盘数据的不足之处。
那么,针对以上情况,我们该如何将我们的数据格式进行优化呢?于是就引出了我们接下来的稀疏数组的概念。
【4】稀疏数组
所谓稀疏数组,从字面上我们也可以得知,它仍然是一个数组,他的作用就是将一个对应的数组数据进行优化,比如,我们将上面的二维数组进行优化。

我们可以定义一个数组,只记录黑白子的数据,具体如下:

从这个表中,我们可以只看到有价值的数据了,如果我们再将这个表抽象为一个二维数组,那么他的大小就是 7 * 3 = 21 个点了。
这相对之前的200多个点来说,确实少了许多节点,节省了很多存储空间。
但只这样保存也是不够的,因为我们并没有保存原棋盘的大小,那么我们不妨约定增加一行,保存原来棋盘的大小(二维数组的大小),那么我们可以用如下进行表示:

这里我们约定用第1行数据来记录原二维数组的规模,第 1 个值为原数组总行数,第 2 个值为原数组列数,第 3 个值为有价值数据的总数。
这样,我们就将原数组数据,优化为了一个稀疏数组。
【4】二维数组转稀疏数组
根据上面的原理,我们就可以通过代码方式,将原来的普通二维数组,优化为一个稀疏数组了,具体编码如下:
/ * 普通数组转稀疏数组 * * @param arr_普通数组 * @return 稀疏数组 */ public static int[][] castSparseArray(int[][] arr) { // 原数组的总行数 int row = arr.length; // 原数组的总列数 int cloumn = arr[0].length; // 获取黑白子总数 int sum = 0; for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { if (arr[i][j] != 0) { sum++; } } } // 创建稀疏数组(sum+1 行表示 sum 个黑白子 + 1行规模) int[][] sparseArray = new int[sum + 1][3]; // 第 1 行原二维数组的行、列、棋子总数 sparseArray[0][0] = row; sparseArray[0][1] = cloumn; sparseArray[0][2] = sum; // 第 2 行开始存具体数据(每行都是一个棋子数据) int sparseRow = 0; for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { if (arr[i][j] != 0) { sparseRow++; sparseArray[sparseRow][0] = i; sparseArray[sparseRow][1] = j; sparseArray[sparseRow][2] = arr[i][j]; } } } return sparseArray; }
【5】稀疏数组转二维数组
我们用稀疏数组保存数据,主要是为了节省磁盘空间,优化 IO 读写性能,但在最终复原棋盘的时候,我们还是需要将稀疏数组的数据转化为原普通二维数组数据,那么我们又将如何通过编码的方式去实现稀疏数组转二维数组呢?对应编码如下:
/ * 稀疏数组转普通数组 * * @param sparseArr_稀疏数组 * @return 普通二维数组 */ public static int[][] castToArray(int[][] sparseArr) { // 获取稀疏数组第 1 行(记录了原数组的总行数和总列数) int[] rowFirst = sparseArr[0]; // 初始化数组(棋盘) int row = rowFirst[0]; int column = rowFirst[1]; int[][] arr = new int[row][column]; // 初始化数据(黑白子) for (int i = 1; i < sparseArr.length; i++) { int[] sparseRow = sparseArr[i]; arr[sparseRow[0]][sparseRow[1]] = sparseRow[2]; } return arr; }
这样,我们就可以将稀疏数组还原为普通数组了。
【总结】
在存储数组数据的时候,我们如果存在许多默认值的数据,不妨用稀疏数组来进行存储,这样数据相对来说就会瘦小很多,不但可以节省磁盘空间,还可以优化磁盘读写时性能。
最后附上相关的整段代码:
/ * 二维数组与稀疏数组 * * @author ZhangYuanqiang * @since 2021年5月13日 */ public class SparseArrayTest { public static void main(String[] args) { // 用二维数组保存棋盘 int[][] arr = saveChess(); // 打印二维数组 print(arr); // 将二维数组转化为稀疏数组 int[][] sparseArr = castSparseArray(arr); // 打印稀疏数组 print(sparseArr); // 稀疏数组转二位数组 int[][] arr2 = castToArray(sparseArr); print(arr2); } / * 打印普通数组 */ public static void print(int[][] arr) { for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { System.out.print(arr[i][j] + "\t"); } System.out.println(); } System.out.println("--------------------------------------"); } / * 将棋盘数据保存为二维数组 */ public static int[][] saveChess() { // 初始化棋盘(大小为 15 * 15) int[][] arr = new int[15][15]; // 保存白子(用1表示) arr[5][5] = 1; arr[7][5] = 1; arr[6][7] = 1; // 保存黑子(用2表示) arr[6][6] = 2; arr[7][6] = 2; arr[8][6] = 2; arr[7][7] = 2; return arr; } / * 普通数组转稀疏数组 * * @param arr_普通数组 * @return 稀疏数组 */ public static int[][] castSparseArray(int[][] arr) { // 原数组的总行数 int row = arr.length; // 原数组的总列数 int cloumn = arr[0].length; // 获取黑白子总数 int sum = 0; for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { if (arr[i][j] != 0) { sum++; } } } // 创建稀疏数组(sum+1 行表示 sum 个黑白子 + 1行规模) int[][] sparseArray = new int[sum + 1][3]; // 第 1 行原二维数组的行、列、棋子总数 sparseArray[0][0] = row; sparseArray[0][1] = cloumn; sparseArray[0][2] = sum; // 第 2 行开始存具体数据(每行都是一个棋子数据) int sparseRow = 0; for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { if (arr[i][j] != 0) { sparseRow++; sparseArray[sparseRow][0] = i; sparseArray[sparseRow][1] = j; sparseArray[sparseRow][2] = arr[i][j]; } } } return sparseArray; } / * 稀疏数组转普通数组 * * @param sparseArr_稀疏数组 * @return 普通二维数组 */ public static int[][] castToArray(int[][] sparseArr) { // 获取稀疏数组第 1 行(记录了原数组的总行数和总列数) int[] rowFirst = sparseArr[0]; // 初始化数组(棋盘) int row = rowFirst[0]; int column = rowFirst[1]; int[][] arr = new int[row][column]; // 初始化数据(黑白子) for (int i = 1; i < sparseArr.length; i++) { int[] sparseRow = sparseArr[i]; arr[sparseRow[0]][sparseRow[1]] = sparseRow[2]; } return arr; } }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/198576.html原文链接:https://javaforall.net
