写在前面
本文思想参考与《Java虚拟机并发编程》,主要讨论的也是并发问题的设计方法,
至于使用到Trie树以及其在字符串匹配方面的数据结构问题不做过多的深入讨论。
持久化数据结构
持久化解决同步问题
在处理同步问题的时候,共享变量的可变性常常带给我们困扰。
这种时候采用持久化的不可变性的数据结构能够使我们在编程的时候无需修改状态就可实现同步问题。
持久化解决同步问题的思想来源于纯粹不可变性设计(参见于Java虚拟机并发编程)
下面贴一条不可变链表的设计与实现
class Node {
public Integer i;
public Node nextNode;
Node(Integer i, Node nextNode) {
this.i = i;
this.nextNode = nextNode;
}
}
/* * 不可变链表实现 * * 与普通i++操作对比 * */
public class IncrementI {
public static Node node = new Node(0, null);//链表头
//每次创建一个新的值为1的节点放在链表头部
public static void Increment() {
Node temp = new Node(1, node);
node = temp;
}
//计算链表中的元素值得总和
public static Integer getCount() {
Integer result = 0;
while (node != null) {
result += node.i;
node = node.nextNode;
}
return result;
}
public static Integer count = 0;
public static void main(String[] args) {
// for (int z = 0; z < 100; z++) {
//
// count = 0;
// node = new Node(0, null);
//
for (int i = 0; i < 10000; i++) {
new Thread() {
public void run() {
count++;
Increment();
};
}.start();
}
System.out.println(count.toString() + "--" + getCount().toString());
// }
}
}
Trie
Trie,又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。
一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。
子节点索引顺序性
Trie的索引顺序性是将节点的key值由其路径决定。
简单举个例子
上面的树种每个节点均有 0 ,1 , 2 三条路径, 每条路径通向的即为节点key值。
以上述结构存储,我们可以有效保存节点顺序和索引信息。
0节点 之所以没有子节点是因为其下面的子节点key值和 其他节点key值重复
其实上述的树结构可以参考计算机网络中的子网划分思想
近似常数时间的插入操作
看到这里你可能会有疑问,不是不可变性的数据结构吗,怎么还有插入操作。
注意这里的不可变性是指不修改已存在的数据,因此插入新的数据我们不能修改已有的树节点
高分支因子结构使其可以保存大量的数据,而树的层数只有有限层数
一个分支因子为32 的Trie结构至多使用4层就可以存储一百万个元素。
还是使用上述的例子,我们插入100 节点时,要做的便是将100的插入路径上的节点进行拷贝替换即可。(这样的操作并不会影响到其他元素)
即,将root ,1 , 10 三个节点拷贝一份并添加上100的引用,以这个新备份替换原来结构中的节点。便完成了插入操作
这样的实际操作导致在100万个元素中插入操作,只影响了四个,所以说这是接近常数操作时间的。
总结
一方面可以在并发时采用,以此来摆脱繁复的同步问题
另一方面在数据库存储方面可能有所贡献
博主所学有限,希望各位能多多指教
如有错误还请指出