Java项目源码学习笔记(二):Path
import java.lang.ref.WeakReference; import java.util.ArrayList; public class Path{ private static Path sRoot = new Path(null, "ROOT"); private final Path mParent; private final String mSegment; private WeakReference<Object> mObject; private IdentityCache<String, Path> mChildren; private Path(Path parent, String segment){ mParent = parent; mSegment = segment; } public Path getChild(String segment){ synchronized(Path.class){ if(mChildren == null){ mChildren = new IdentityCache<String, Path>(); }else{ Path p = mChildren.get(segment); if(p != null)return p; } Path p = new Path(this, segment); mChildren.put(segment, p); return p; } } public Path getParent(){ synchronized(Path.class){ return mParent; } } public void setObject(Object object){ synchronized(Path.class){ mObject = new WeakReference<Object>(object); } } public Object getObject(){ synchronized(Path.class){ return (mObject == null) ? null : mObject.get(); } } @Override public String toString(){ synchronized(Path.class){ StringBuilder sb = new StringBuilder(); String[] segments = split(); for(int i = 0; i < segments.length; i++){ sb.append("/"); sb.append(segments[i]); } return sb.toString(); } } public static Path fromString(String s){ synchronized(Path.class){ String[] segments = split(s); Path current = sRoot; for(int i =0; i < segments.length; i++){ current = current.getChild(segments[i]); } return current; } } public String[] split(){ synchronized(Path.class){ int n = 0; for(Path p = this; p != sRoot; p = p.mParent){ n++; } String[] segments = new String[n]; int i = n - 1; for(Path p = this; p != sRoot; p = p.mParent){ segments[i--] = p.mSegment; } return segments; } } public static String[] split(String s){ int n = s.length(); if(n == 0)return new String[0]; if(s.charAt(0) != '/'){ throw new RuntimeException("malformed pa"); } ArrayList<String> segments = new ArrayList<String>(); int i = 1; while(i < n){ int brace = 0; int j; for(j = i; j < n; j++){ char c = s.charAt(j); if(c == '{')++brace; else if(c == '}')--brace; else if(brace == 0 && c == '/')break; } if(brace != 0){ throw new RuntimeException("un"); } segments.add(s.substring(i,j)); i = j + 1; } String[] result = new String[segments.size()]; segments.toArray(result); return result; } }
细节说明:
1. 构造函数是private的,这是因为Path要构建的是一个数据链表,而不仅仅是构造函数本身构建的对象。
Path对象节点结构如图:它包括一个引用指向mParent,同时包含一个容器对象mChildren,这个容器对象中保存着指向子节点的引用,这个引用与mSegment构成容器的键值对。
如上可知这是一个双向链表数据结构
2.
public Path getChild(String segment){ synchronized(Path.class){ if(mChildren == null){ mChildren = new IdentityCache<String, Path>(); }else{ Path p = mChildren.get(segment); if(p != null)return p; } Path p = new Path(this, segment); mChildren.put(segment, p); return p; } }
分析一个函数,可以把它代入到实际的应用场景中去:
结合下一个将要说明的public static Path fromString(String s),我们知道Path链表数据对象是由getChild函数构建起来的,所以在构建对象的时候mChildren肯定是null的。例如String s = "/aaa/bbb/123"构建的path数据对象如下
path1, path2, path3三个对象便是通过getChild()生成的。
看看上面的链表,这是String s = "/aaa/bbb/123"对应的Path对象的样子。
之所以用双向链表替代String,则是因为需要利用双向链表的结构优势:假设Path p = Path.fromString(“/aaa/bbb”)表示一个集合,那么Path child = p.getChild("123")则可以表示这个集合对应的一个子集。
3.
public static Path fromString(String s){ synchronized(Path.class){ String[] segments = split(s); Path current = sRoot; for(int i =0; i < segments.length; i++){ current = current.getChild(segments[i]); } return current; } }
需要说明的是如2中的表格,通过这个静态类生成的path是最后一个child:path3,但是这里要再次申明这是一个链表结构数据,而不是单纯的只看当前对象。
细节说明:(a)、synchronzied(Path.class),这是类中静态函数是并发函数时的保护方式;(b)、fromString是static的,getChild是非static的,这里很好的诠释了静态函数中如何调用非静态方法——显然静态方法是类相关的,非静态方法是对象相关的,因此非静态方法可以直接调用静态方法,这样并不会产生错误,而静态方法要调用非静态方法必须是对象相关的,而不能直接调用方法。