压缩、序列化与哈希

压缩和解压缩

压缩: 把数据换一种方式来存储,以减小存储空间。

解压缩: 把压缩后的数据还原为原先的形式,以便使用。

常见的压缩算法有: DEFLATE、JPEG、MP3。

其中 DEFLATE 就是 ZIP 的压缩算法,ZIP 是我们常见的打包格式。其正式说法叫做 Archive(归档),将多个文件打包成一个文件,方便传输和存储。

我们来看看什么是数据压缩。

比如对于如下数据:

复制代码
AAAABBBCCDEEEE

我可以压缩为:4A3B2C1D4E,这种压缩算法非常粗暴,如果原始数据变为这样:

复制代码
ABCDE

压缩结果:1A1B1C1D1E 反而比原始数据大。一个优秀的压缩算法,它能在常见数据下实现高压缩率。不过,你需要知道的是不存在一种能够压缩任意数据的无损算法。对于一串完全随机的数据,经过无损压缩算法处理后,体积可能会变大。

当然对于上述的压缩结果,我们还能还原为原数据。例如 4A3B2C1D4E,我们只需根据字符的出现次数以及对应的字符,即可还原为 AAAABBBCCDEEEE

那压缩属于编码吗?

其实编码是没有一个官方标准的。通常来说,我们认为的编码是使用一套固定的规则,将数据从 A 格式转换为 B 格式,并且还能复原,不损失任何数据。这么来说,压缩就属于编码。

媒体数据的编解码

媒体数据指的是将音视频、图片等数据。

以图片的编解码为例。

图片的编码:把图片数据转成 JPG、PNG 等文件的编码格式。

图片的解码:把 JPG、PNG 等文件中的数据解析成标准的图像数据。

另外,媒体数据的压缩通常是有损压缩。它之所以可行,是因为利用了我们无法察觉到图片或音频中的所有细节。有损压缩就是通过丢弃了这些我们感知不到的数据,从而实现了更高的压缩率。MP3 就是一个典型的例子,正因为有损压缩后的数据无法还原为原数据,所以有损压缩并不算我们平常所说的编码。

序列化

在 Java 中,我们经常需要让某个类实现 Serializable 接口,使其能够序列化。

而这个序列化,其实是把处于内存中的数据对象转换成字节序列的过程。

例如:我们会将 zhangsan 这个对象转为一段连续的数据,使其能够存储到文件或是在网络上进行传输。

kotlin 复制代码
data class Human(val name: String, val age: Int, val gender: String, val mate: Human? = null)

val zhangsan = Human("张三", 18, "男", Human("王五", 20, "女"))

比如我们转为 JSON 格式。

kotlin 复制代码
{"name":"张三","age":18,"gender":"男","mate":{"name":"王五","age":20,"gender":"女"}}

也可以是这样的:

kotlin 复制代码
Human(name=张三, age=18, gender=男, mate=Human(name=王五, age=20, gender=女, mate=null))

只要将内存中的这个对象转成序列,就叫序列化。不一定非得是实现了某个接口,调用了某个方法。

反序列化就是将序列化后的字节序列重新转为内存中的对象。

为什么需要序列化?

之前也说过了,就是要让内存中的对象能够被存储、被传输。根本原因是,内存中的对象结构很复杂,可能由多个部分组成,通过引用进行关联,散落在了内存的不同地方。而往磁盘存文件或者在网络上传输,都需要一段连续的字节数据。序列化,就是将内存中这些零散的对象结构,转换成一段连续字节流的过程。

Hash

Hash 的原义是 "切碎"、"拼凑" 的意思。而在程序中,意思是哈希函数,它可以把任意数据转换成一个固定长度的字符串(哈希值)。

Hash 值可用于摘要和数字指纹。

经典算法有: MD5、SHA1、SHA256 等。

哈希值为什么需要根据特定算法得出?

因为好的哈希函数有以下特点:

  • 相同的输入,必定得到相同的输出。

  • 对于不同的输入,能够尽可能产生不同的输出,避免碰撞(冲突)。

  • 输入数据的微小变化,会导致输出具有差异。

对于不同的输入,如果它们的哈希值相同,这就叫发生了哈希碰撞。比如我们设计了一个哈希算法:hash(String s) = s.length()。使用它来计算 "hello""world" 的哈希值,发现都是 5,这就发生了一次哈希碰撞。好的哈希算法会让这种碰撞的概率变得非常低。

Hash 的实际用途主要有:

  • 数据完整性的验证

    比如对于一个重要文件,如果你计算它的哈希值与其标定的哈希值相同,那么你就可以认为这份文件未被篡改,是完整的。

  • 身份的快速匹配/数据的快速比较

    我们在重写类的 equals 方法时,通常要同时重写 hashCode 方法。这是因为 HashMap 等这类容器,在判断元素是否存在时,为了效率,会先使用 hashCode 方法进行比较。因为如果两个对象的 hashCode 值不同,那么这两个对象一定不相等 。这样可以快速排除掉大部分不同的元素。只有当 hashCode 值相同时,再使用 equals 方法做最终判断,因为 hashCode 值相同不代表两个对象就一定相等,有可能是发生了哈希碰撞。

    为什么要用 hashCode 方法,而不直接使用 equals 方法进行判断?

    这是因为 hashCode 的运算非常快,用于判断两个元素不相等,最合适不过了。

另外,目前密码的存储都是将密码的哈希值存入数据库。并不是进行明文存储,因为有泄露风险。密码进行验证时,只需将输入的密码进行 Hash,再与数据库中存的哈希值进行比对,就能够判断密码是否正确。

这样,即使数据库泄露了,被别人拿到了用户密码的哈希值,别人也无法获取到真实的密码,因为 Hash 运算是不可逆的。不过,别人也可能会使用彩虹表来破解密码。

彩虹表是将常见的密码与它们的哈希值存到数据库中,这样别人就可以通过已知的哈希值,来推导出真实的密码了。也有解决方法,在计算密码的哈希值之前,通常会给密码加上盐,也就是给密码附加上额外的数据,这个数据可以是固定的,也可以是随机的。这样,别人即使知道一些常用密码的哈希值,也是无法破解密码的,因为存入数据库的值,并不是真实密码的哈希值,而是加上了额外数据计算出的哈希值。

例如:你的真实密码是 136520。我并不会计算当前字符串的哈希值,而是会加上某个字符串,如 zxc,再计算加上后的字符串的哈希值,存入到数据库中。

最后说说 Hash 是编码吗,是加密吗?

  • Hash 并不是编码,因为它只是原数据的摘要,并不能通过它来还原数据。

  • Hash 也不是加密,MD5 常常被认为是 "不可逆加密"。但加密的定义是,使用加密算法对数据进行加密隐藏原文,然后能够使用解密算法还原数据。而 Hash 的目的是生成数据的摘要进行验证,是单向的,无法还原数据。所以 MD5 等算法并不是加密算法。

字符集

字符集:一个由整数向现实世界中的文字符号的 Map。

例如这就是一个非常简单的字符集:

kotlin 复制代码
1 => A
2 => B
3 => C

最早的字符集是 ASCII,它包含了 128 个字符,每个字符占 1 字节。然后出现了 ISO-8859-1 字符集,它对 ASCII 字符集进行了扩充,每个字符也是只占 1 字节。接着有了大名鼎鼎的 Unicode 字符集,开始对拉丁字母以外的字符有了支持,比如中文、日文等。

Unicode 字符集有两个著名的编码分支,分别是:UTF-8 和 UTF-16。

编码分支是什么意思呢?在 Unicode 字符集中,规定 U+4E2D 代表 "中" 字,而 UTF-8 和 UTF-16 的实现方式则有些不同。比如 UTF-8 可能会使用 E4 B8 AD 三个字节来代表 "中",而 UTF-16 可能会用两个字节 4E 2D 来表示。虽然它们表达的是同一个字,但占用的字节数和字节内容不同。这就是同一个字符集下的不同编码实现。

然后还有 GBK / GB2312 / GB18030 系列,它们既是字符集标准,也包含了编码规则。这是中国自研的标准,不过现在全世界最广泛使用的是 Unicode 字符集和 UTF-8 编码。

相关推荐
前行的小黑炭7 分钟前
Android App:每次想写新项目锻炼一下,但苦于没有UI,那么这篇文章适合你~(适合基础小白锻炼)
android·kotlin
安卓开发者3 小时前
RxJava 核心概念解析:构建响应式Android应用的基石
android·echarts·rxjava
叽哥3 小时前
flutter学习第 18 节:设备功能调用
android·flutter·ios
Monkey-旭4 小时前
Android ADB 常用指令全解析
android·adb
来来走走5 小时前
Flutter 顶部导航标签组件Tab + TabBar + TabController
android·flutter
丐中丐9995 小时前
Android NFC框架的NfcService与hal层代码概览
android
用户2018792831676 小时前
<include>标签时设置ltr无效?
android
用户2018792831676 小时前
Android多语言与RTL/LTR适配
android
minos.cpp7 小时前
第一章 OkHttp 是怎么发出一个请求的?——整体流程概览
android·okhttp·面试