publicclassRTree<T>{publicenumSeedPicker{LINEAR,QUADRATIC}/** * 每个节点包含的最大entry数目 */privatefinalintM;/** * 每个节点包含的最小entry数目 */privatefinalintm;/** * 空间的维度,如平面则为2,空间点则为3 */privatefinalintD;privatefinalfloat[]pointDims;privatefinalSeedPickerseedPicker;/** * 根节点 */privateNoderoot;privatevolatileintsize;/** * 构造默认R-Tree的参数 * DEFAULT_M : M * DEFAULT_m : m * DEFAULT_D : D */privatestaticfinalintDEFAULT_M=50;privatestaticfinalintDEFAULT_m=2;privatestaticfinalintDEFAULT_D=2;/** * 创建一颗新的R-Tree * * @param M maximum number of entries per node * @param m minimum number of entries per node (except for the root node) * @param D the number of dimensions of the RTree. */publicRTree(intM,intm,intD,SeedPickerseedPicker){assert(m<=(M/2));this.D=D;this.M=M;this.m=m;this.seedPicker=seedPicker;pointDims=newfloat[D];root=buildRoot(true);}publicRTree(intM,intm,intD){this(M,m,D,SeedPicker.LINEAR);}/** * 使用默认参数构造一个R-Tree */publicRTree(){this(DEFAULT_M,DEFAULT_m,DEFAULT_D,SeedPicker.LINEAR);}/** * 构造根节点 * * @param asLeaf : 根节点是否为叶子节点 * @return */privateNodebuildRoot(booleanasLeaf){float[]initCoords=newfloat[D];float[]initDimensions=newfloat[D];for(inti=0;i<this.D;i++){initCoords[i]=(float)Math.sqrt(Float.MAX_VALUE);initDimensions[i]=-2.0f*(float)Math.sqrt(Float.MAX_VALUE);}returnnewNode(initCoords,initDimensions,asLeaf);}/** * getter */publicintgetMaxEntries(){returnM;}publicintgetMinEntries(){returnm;}publicintgetNumDims(){returnD;}publicintsize(){returnsize;}/** * 在R-Tee中搜索和给定矩形有重叠(overlapping)的对象 * * @param coords 矩形的一个顶点(比如左上角) * @param dimensions 矩形长度. * 返回对象List,这些对象的最小边界矩形(MBR)和给定矩形重叠 */publicList<T>search(float[]coords,float[]dimensions){assert(coords.length==D);assert(dimensions.length==D);LinkedList<T>results=newLinkedList<T>();search(coords,dimensions,root,results);returnresults;}privatevoidsearch(float[]coords,float[]dimensions,Noden,LinkedList<T>results){if(n.leaf){for(Nodee:n.children){if(isOverlap(coords,dimensions,e.coords,e.dimensions)){results.add(((Entry)e).entry);}}}else{for(Nodec:n.children){if(isOverlap(coords,dimensions,c.coords,c.dimensions)){search(coords,dimensions,c,results);//继续在孩子节点中搜索}}}}/** * 删除R-Tree中在矩形rect内的数据entry * * @param coords : 矩形的一个顶点(比如左上角) * @param dimensions : 矩形长度 * @param entry : 待删除数据 * 删除成功返回true */publicbooleandelete(float[]coords,float[]dimensions,Tentry){assert(coords.length==D);assert(dimensions.length==D);Nodel=findLeaf(root,coords,dimensions,entry);if(l==null){System.out.println("WTF?");findLeaf(root,coords,dimensions,entry);}assert(l!=null):"Could not find leaf for entry to delete";assert(l.leaf):"Entry is not found at leaf?!?";ListIterator<Node>li=l.children.listIterator();Tremoved=null;while(li.hasNext()){@SuppressWarnings("unchecked")Entrye=(Entry)li.next();if(e.entry.equals(entry)){removed=e.entry;li.remove();break;}}if(removed!=null){condenseTree(l);size--;}if(size==0){root=buildRoot(true);}return(removed!=null);}publicbooleandelete(float[]coords,Tentry){returndelete(coords,pointDims,entry);}/** * 查找数据 entry 对应的叶子节点 * @param n * @param coords * @param dimensions * @param entry * @return */privateNodefindLeaf(Noden,float[]coords,float[]dimensions,Tentry){if(n.leaf){for(Nodec:n.children){if(((Entry)c).entry.equals(entry)){returnn;}}returnnull;}else{for(Nodec:n.children){if(isOverlap(c.coords,c.dimensions,coords,dimensions)){Noderesult=findLeaf(c,coords,dimensions,entry);if(result!=null){returnresult;}}}returnnull;}}/** * 压缩树 * @param n */privatevoidcondenseTree(Noden){Set<Node>q=newHashSet<Node>();while(n!=root){if(n.leaf&&(n.children.size()<m)){q.addAll(n.children);n.parent.children.remove(n);}elseif(!n.leaf&&(n.children.size()<m)){// probably a more efficient way to do this...LinkedList<Node>toVisit=newLinkedList<Node>(n.children);while(!toVisit.isEmpty()){Nodec=toVisit.pop();if(c.leaf){q.addAll(c.children);}else{toVisit.addAll(c.children);}}n.parent.children.remove(n);}else{tighten(n);}n=n.parent;}if(root.children.size()==0){root=buildRoot(true);}elseif((root.children.size()==1)&&(!root.leaf)){root=root.children.get(0);root.parent=null;}else{tighten(root);}for(Nodene:q){@SuppressWarnings("unchecked")Entrye=(Entry)ne;insert(e.coords,e.dimensions,e.entry);}size-=q.size();}/** * 清空整个R-Tree */publicvoidclear(){root=buildRoot(true);//让GC做剩余的事情...}/** * 在RTree中插入数据entry, 和矩形匹配. * * @param coords : 矩形的一个顶点(比如左上角) * @param dimensions : 矩形长度 * @param entry : 待出入数据 */publicvoidinsert(float[]coords,float[]dimensions,Tentry){assert(coords.length==D);assert(dimensions.length==D);Entrye=newEntry(coords,dimensions,entry);Nodel=chooseLeaf(root,e);l.children.add(e);size++;e.parent=l;if(l.children.size()>M){Node[]splits=splitNode(l);adjustTree(splits[0],splits[1]);}else{adjustTree(l,null);}}/** * Convenience method for inserting a point * * @param coords * @param entry */publicvoidinsert(float[]coords,Tentry){insert(coords,pointDims,entry);}privatevoidadjustTree(Noden,Nodenn){if(n==root){if(nn!=null){// build new root and add children.root=buildRoot(false);root.children.add(n);n.parent=root;root.children.add(nn);nn.parent=root;}tighten(root);return;}tighten(n);if(nn!=null){tighten(nn);if(n.parent.children.size()>M){Node[]splits=splitNode(n.parent);adjustTree(splits[0],splits[1]);}}if(n.parent!=null){adjustTree(n.parent,null);}}privateNode[]splitNode(Noden){// TODO: this class probably calls "tighten" a little too often.// For instance the call at the end of the "while (!cc.isEmpty())" loop// could be modified and inlined because it's only adjusting for the addition// of a single node. Left as-is for now for readability.@SuppressWarnings("unchecked")Node[]nn=newRTree.Node[]{n,newNode(n.coords,n.dimensions,n.leaf)};nn[1].parent=n.parent;if(nn[1].parent!=null){nn[1].parent.children.add(nn[1]);}LinkedList<Node>cc=newLinkedList<Node>(n.children);n.children.clear();Node[]ss=seedPicker==SeedPicker.LINEAR?lPickSeeds(cc):qPickSeeds(cc);nn[0].children.add(ss[0]);nn[1].children.add(ss[1]);tighten(nn);while(!cc.isEmpty()){if((nn[0].children.size()>=m)&&(nn[1].children.size()+cc.size()==m)){nn[1].children.addAll(cc);cc.clear();tighten(nn);// Not sure this is required.returnnn;}elseif((nn[1].children.size()>=m)&&(nn[0].children.size()+cc.size()==m)){nn[0].children.addAll(cc);cc.clear();tighten(nn);// Not sure this is required.returnnn;}Nodec=seedPicker==SeedPicker.LINEAR?lPickNext(cc):qPickNext(cc,nn);Nodepreferred;floate0=getRequiredExpansion(nn[0].coords,nn[0].dimensions,c);floate1=getRequiredExpansion(nn[1].coords,nn[1].dimensions,c);if(e0<e1){preferred=nn[0];}elseif(e0>e1){preferred=nn[1];}else{floata0=getArea(nn[0].dimensions);floata1=getArea(nn[1].dimensions);if(a0<a1){preferred=nn[0];}elseif(e0>a1){preferred=nn[1];}else{if(nn[0].children.size()<nn[1].children.size()){preferred=nn[0];}elseif(nn[0].children.size()>nn[1].children.size()){preferred=nn[1];}else{preferred=nn[(int)Math.round(Math.random())];}}}preferred.children.add(c);tighten(preferred);}returnnn;}// Implementation of Quadratic PickSeedsprivateRTree<T>.Node[]qPickSeeds(LinkedList<Node>nn){@SuppressWarnings("unchecked")RTree<T>.Node[]bestPair=newRTree.Node[2];floatmaxWaste=-1.0f*Float.MAX_VALUE;for(Noden1:nn){for(Noden2:nn){if(n1==n2)continue;floatn1a=getArea(n1.dimensions);floatn2a=getArea(n2.dimensions);floatja=1.0f;for(inti=0;i<D;i++){floatjc0=Math.min(n1.coords[i],n2.coords[i]);floatjc1=Math.max(n1.coords[i]+n1.dimensions[i],n2.coords[i]+n2.dimensions[i]);ja*=(jc1-jc0);}floatwaste=ja-n1a-n2a;if(waste>maxWaste){maxWaste=waste;bestPair[0]=n1;bestPair[1]=n2;}}}nn.remove(bestPair[0]);nn.remove(bestPair[1]);returnbestPair;}/** * Implementation of QuadraticPickNext * * @param cc the children to be divided between the new nodes, one item will be removed from this list. * @param nn the candidate nodes for the children to be added to. */privateNodeqPickNext(LinkedList<Node>cc,Node[]nn){floatmaxDiff=-1.0f*Float.MAX_VALUE;NodenextC=null;for(Nodec:cc){floatn0Exp=getRequiredExpansion(nn[0].coords,nn[0].dimensions,c);floatn1Exp=getRequiredExpansion(nn[1].coords,nn[1].dimensions,c);floatdiff=Math.abs(n1Exp-n0Exp);if(diff>maxDiff){maxDiff=diff;nextC=c;}}assert(nextC!=null):"No node selected from qPickNext";cc.remove(nextC);returnnextC;}// Implementation of LinearPickSeedsprivateRTree<T>.Node[]lPickSeeds(LinkedList<Node>nn){@SuppressWarnings("unchecked")RTree<T>.Node[]bestPair=newRTree.Node[2];booleanfoundBestPair=false;floatbestSep=0.0f;for(inti=0;i<D;i++){floatdimLb=Float.MAX_VALUE,dimMinUb=Float.MAX_VALUE;floatdimUb=-1.0f*Float.MAX_VALUE,dimMaxLb=-1.0f*Float.MAX_VALUE;NodenMaxLb=null,nMinUb=null;for(Noden:nn){if(n.coords[i]<dimLb){dimLb=n.coords[i];}if(n.dimensions[i]+n.coords[i]>dimUb){dimUb=n.dimensions[i]+n.coords[i];}if(n.coords[i]>dimMaxLb){dimMaxLb=n.coords[i];nMaxLb=n;}if(n.dimensions[i]+n.coords[i]<dimMinUb){dimMinUb=n.dimensions[i]+n.coords[i];nMinUb=n;}}floatsep=(nMaxLb==nMinUb)?-1.0f:Math.abs((dimMinUb-dimMaxLb)/(dimUb-dimLb));if(sep>=bestSep){bestPair[0]=nMaxLb;bestPair[1]=nMinUb;bestSep=sep;foundBestPair=true;}}// In the degenerate case where all points are the same, the above// algorithm does not find a best pair. Just pick the first 2// children.if(!foundBestPair){bestPair=newRTree.Node[]{nn.get(0),nn.get(1)};}nn.remove(bestPair[0]);nn.remove(bestPair[1]);returnbestPair;}/** * Implementation of LinearPickNext * * @param cc the children to be divided between the new nodes, one item will be removed from this list. */privateNodelPickNext(LinkedList<Node>cc){returncc.pop();}privatevoidtighten(Node...nodes){assert(nodes.length>=1):"Pass some nodes to tighten!";for(Noden:nodes){assert(n.children.size()>0):"tighten() called on empty node!";float[]minCoords=newfloat[D];float[]maxCoords=newfloat[D];for(inti=0;i<D;i++){minCoords[i]=Float.MAX_VALUE;maxCoords[i]=Float.MIN_VALUE;for(Nodec:n.children){// we may have bulk-added a bunch of children to a node (eg. in// splitNode)// so here we just enforce the child->parent relationship.c.parent=n;if(c.coords[i]<minCoords[i]){minCoords[i]=c.coords[i];}if((c.coords[i]+c.dimensions[i])>maxCoords[i]){maxCoords[i]=(c.coords[i]+c.dimensions[i]);}}}for(inti=0;i<D;i++){// Convert max coords to dimensionsmaxCoords[i]-=minCoords[i];}System.arraycopy(minCoords,0,n.coords,0,D);System.arraycopy(maxCoords,0,n.dimensions,0,D);}}privateRTree<T>.NodechooseLeaf(RTree<T>.Noden,RTree<T>.Entrye){if(n.leaf){returnn;}floatminInc=Float.MAX_VALUE;Nodenext=null;for(RTree<T>.Nodec:n.children){floatinc=getRequiredExpansion(c.coords,c.dimensions,e);if(inc<minInc){minInc=inc;next=c;}elseif(inc==minInc){floatcurArea=1.0f;floatthisArea=1.0f;for(inti=0;i<c.dimensions.length;i++){curArea*=next.dimensions[i];thisArea*=c.dimensions[i];}if(thisArea<curArea){next=c;}}}returnchooseLeaf(next,e);}/** * Returns the increase in area necessary for the given rectangle to cover the given entry. */privatefloatgetRequiredExpansion(float[]coords,float[]dimensions,Nodee){floatarea=getArea(dimensions);float[]deltas=newfloat[dimensions.length];for(inti=0;i<deltas.length;i++){if(coords[i]+dimensions[i]<e.coords[i]+e.dimensions[i]){deltas[i]=e.coords[i]+e.dimensions[i]-coords[i]-dimensions[i];}elseif(coords[i]+dimensions[i]>e.coords[i]+e.dimensions[i]){deltas[i]=coords[i]-e.coords[i];}}floatexpanded=1.0f;for(inti=0;i<dimensions.length;i++){expanded*=dimensions[i]+deltas[i];}return(expanded-area);}privatefloatgetArea(float[]dimensions){floatarea=1.0f;for(inti=0;i<dimensions.length;i++){area*=dimensions[i];}returnarea;}privatebooleanisOverlap(float[]scoords,float[]sdimensions,float[]coords,float[]dimensions){finalfloatFUDGE_FACTOR=1.001f;for(inti=0;i<scoords.length;i++){booleanoverlapInThisDimension=false;if(scoords[i]==coords[i]){overlapInThisDimension=true;}elseif(scoords[i]<coords[i]){if(scoords[i]+FUDGE_FACTOR*sdimensions[i]>=coords[i]){overlapInThisDimension=true;}}elseif(scoords[i]>coords[i]){if(coords[i]+FUDGE_FACTOR*dimensions[i]>=scoords[i]){overlapInThisDimension=true;}}if(!overlapInThisDimension){returnfalse;}}returntrue;}/** * 节点 */privateclassNode{/** * 高维矩形的一个定点,如左下角 */finalfloat[]coords;/** * 矩形的长度 */finalfloat[]dimensions;/** * 孩子节点 */finalLinkedList<Node>children;/** * 是否为叶子节点 */finalbooleanleaf;/** * 父亲节点 */Nodeparent;privateNode(float[]coords,float[]dimensions,booleanleaf){this.coords=newfloat[coords.length];this.dimensions=newfloat[dimensions.length];System.arraycopy(coords,0,this.coords,0,coords.length);System.arraycopy(dimensions,0,this.dimensions,0,dimensions.length);this.leaf=leaf;children=newLinkedList<Node>();}}/** * 实体,代表一个数据项 * 注意:不是叶子节点,其父亲才是叶子节点 */privateclassEntryextendsNode{/** * 数据 */finalTentry;publicEntry(float[]coords,float[]dimensions,Tentry){super(coords,dimensions,true);this.entry=entry;}publicStringtoString(){return"Entry: "+entry;}}}
Queue的长度可以有限,也可以无限.BlockingQueue的实现包括:LinkedBlockingQueue和ArrayBlockingQueue是FIFO队列;PriorityBlockingQueue是一个按优先级顺序排序的队列,支持自定义Comparator;SynchronousQueue根本上不是一个整整意义的队列,因为它不会为队列元素维护任何存储空间.其中每个 put 必须等待一个 take,反之亦然.
publicclassFileCrawlerimplementsRunnable{privatefinalBlockingQueue<File>fileQueue;privatefinalFileroot;publicFileCrawler(BlockingQueue<File>fileQueue,Fileroot){this.fileQueue=fileQueue;this.root=root;}@Overridepublicvoidrun(){try{crawl(root);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}privatevoidcrawl(Fileroot)throwsInterruptedException{File[]files=root.listFiles();if(files!=null&&files.length>0){for(Filefile:files){if(file.isDirectory())crawl(file);elseif(alreadyIndexed(file)==false)fileQueue.put(file);}}}privatebooleanalreadyIndexed(Filefile){//TODOreturnfalse;}}publicclassFileIndexerimplementsRunnable{privatefinalBlockingQueue<File>fileQueue;publicFileIndexer(BlockingQueue<File>fileQueue){this.fileQueue=fileQueue;}@Overridepublicvoidrun(){try{while(true)index(fileQueue.take());}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}privatevoidindex(Filefile){//TODO : do index}}publicclassCrawlAndIndex{publicstaticvoidmain(String[]args){finalintMAX_QUEUE_SIZE=1024;finalintINDEXER_COUNT=100;finalFile[]roots=getFiles();BlockingQueue<File>fileQueue=newLinkedBlockingDeque<File>(MAX_QUEUE_SIZE);for(Filefile:roots)newThread(newFileCrawler(fileQueue,file)).start();for(inti=0;i<INDEXER_COUNT;i++)newThread(newFileIndexer(fileQueue)).start();}privatestaticFile[]getFiles(){//TODOreturnnull;}}
classSynchronizedCollection<E>implementsCollection<E>,Serializable{finalCollection<E>c;// Backing CollectionfinalObjectmutex;// Object on which to synchronize...publicbooleanadd(Ee){synchronized(mutex){returnc.add(e);}}...
staticclassSynchronizedCollection<E>implementsCollection<E>,Serializable{finalCollection<E>c;// Backing CollectionfinalObjectmutex;// Object on which to synchronize...}