IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> zookeeper源码解析(八) -> 正文阅读

[大数据]zookeeper源码解析(八)

2021SC@SDUSC

前言

本节介绍PathTrie类,它是zookeeper中对字典树的实现(传统的字典树一个节点或者一条边存储的是一个字符,这里则是对路径分割后的字符串),在DataTree类中用于记录配额节点。

PathTrie

成员变量:

     //该类的日志
     private static final Logger LOG = LoggerFactory.getLogger(PathTrie.class);

     //根节点
     private final TrieNode rootNode;

     //这里用了java的并发包提供的读写锁ReentrantReadWriteLock,且构造参数为true,使用的是公平锁
     private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
        
     //读取操作的锁
     private final Lock readLock = lock.readLock();

     //写入操作的锁
     private final Lock writeLock = lock.writeLock();

构造函数:

    //创建根节点
    public PathTrie() {
         //根节点没有父节点,参数为null,存储的值为"/"
         this.rootNode = new TrieNode(null, "/");
     }

addPath:

    
    //将对应路径加入字典树,路径都是相对根节点而言的
    public void addPath(final String path) {
         //path不能为null
         Objects.requireNonNull(path, "Path cannot be null");

         //path不能为空字符串
         if (path.length() == 0) {
             throw new IllegalArgumentException("Invalid path: " + path);
         }
         //将路径切割
         final String[] pathComponents = split(path);
         
         //使用写锁
         writeLock.lock();

         try {
             TrieNode parent = rootNode;
             //遍历刚才切割后得到的string数组
             for (final String part : pathComponents) {
                 //如果已经有节点,那么就直接使用,如果没有,就创建对应节点
                 TrieNode child = parent.getChild(part);
                 if (child == null) {
                     child = new TrieNode(parent, part);
                     parent.addChild(part, child);
                 }
                 //递归,自顶向下安排节点
                 parent = child;
             }
             //设置属性为true,字典树中一个节点属性为true,表明该“单词”存在(在此处则是指路径存在)
             parent.setProperty(true);
         } finally {
             //释放写锁
             writeLock.unlock();
         }
     }

deletePath:

    //从字典树中删除路径,路径均是相对根节点而言的
    public void deletePath(final String path) {
         //path不能为null
         Objects.requireNonNull(path, "Path cannot be null");

         //path不能为空字符串
         if (path.length() == 0) {
             throw new IllegalArgumentException("Invalid path: " + path);
         }

         //对路径切割得到字符串数组
         final String[] pathComponents = split(path);

         //使用写锁
         writeLock.lock();

         try {
             TrieNode parent = rootNode;
             //遍历对路径切割后得到的字符串数组
             for (final String part : pathComponents) {
                 if (parent.getChild(part) == null) {
                     //如果在树中找不到对应节点,表明树中根本没有存储这个路径,直接返回
                     return;
                 }
                 //字典树中查找“单词”是否存在的基本操作,从根节点开始一步步向下看构成单词的字符是否存在,在此处“单词”指“路径”,而“构成单词的字符”就是“对路径切割后的字符串数组”
                 parent = parent.getChild(part);
                 LOG.debug("{}", parent);
             }
             //获取“单词的最后一个字符”对应节点的父节点
             final TrieNode realParent = parent.getParent();
             //字典树意义上的删除,节点的property属性置为false(表明字典树中没有这个路径),但节点未必删除,因为该节点可能还有子节点(即该节点不是叶子节点)
             realParent.deleteChild(parent.getValue());
         } finally {
             //释放写锁
             writeLock.unlock();
         }
     }

existsNode:

    //判断字典树是否存在指定路径,路径是相对根节点而言的
    public boolean existsNode(final String path) {
         //同样地,路径不能是null,不能是空字符串
         Objects.requireNonNull(path, "Path cannot be null");

         if (path.length() == 0) {
             throw new IllegalArgumentException("Invalid path: " + path);
         }
         //路径字符串切割
         final String[] pathComponents = split(path);
         
         //使用读锁,这里不涉及写操作,可以共享
         readLock.lock();
         try {
             //这里和deletePath方法中的过程是一致的,字典树判断指定“单词”是否存在的典型方法
             TrieNode parent = rootNode;
             for (final String part : pathComponents) {
                 if (parent.getChild(part) == null) {
                     
                     return false;
                 }
                 parent = parent.getChild(part);
                 LOG.debug("{}", parent);
             }
         } finally {
             //释放读锁
             readLock.unlock();
         }
         return true;
     }

findMaxPrefix:

    //返回指定路径的最大前缀(需要在字典树中是存在的),路径都是相对根节点而言的
    public String findMaxPrefix(final String path) {
         //路径不能为null
         Objects.requireNonNull(path, "Path cannot be null");

         //对路径切割
         final String[] pathComponents = split(path);
        
         //不涉及写操作,使用读锁
         readLock.lock();
         try {
             TrieNode parent = rootNode;
             TrieNode deepestPropertyNode = null;
             //对字典树做“查找单词是否存在”的遍历,若某个“字符”不存在,则退出循环,在这个过程中记录遍历过程中符合property为true(即在字典树中存在)的最后访问的节点
             for (final String element : pathComponents) {
                 parent = parent.getChild(element);
                 if (parent == null) {
                     LOG.debug("{}", element);
                     break;
                 }
                 if (parent.hasProperty()) {
                     deepestPropertyNode = parent;
                 }
             }
             //如果遍历过程中没有property为true的节点,返回“/”
             if (deepestPropertyNode == null) {
                 return "/";
             }

             final Deque<String> treePath = new ArrayDeque<>();
             TrieNode node = deepestPropertyNode;
             //从deepestPropertyNode向根节点回溯,从而打印出最大前缀路径
             while (node != this.rootNode) {
                 //插入头部
                 treePath.offerFirst(node.getValue());
                 node = node.parent;
             }
             return "/" + String.join("/", treePath);
         } finally {
             //释放读锁
             readLock.unlock();
         }
     }

clear:

    //清空节点
    public void clear() {
         //使用写锁
         writeLock.lock();
         try {
             //根节点的子节点都清空
             rootNode.getChildren().clear();
         } finally {
             //释放写锁
             writeLock.unlock();
         }
     }

split:
?

    //用于对路径字符串做切割,该方法保证了一定的健壮性,可以处理路径中含空白符的情况
    private static String[] split(final String path){
         return Stream.of(path.split("/"))
                 .filter(t -> !t.trim().isEmpty())
                 .toArray(String[]::new);
     }

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:24:30  更:2021-11-22 12:25:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:23:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码