我经常不厌其烦地向开发人员强调数据结构的重要性,也经常和团队分享一些常见算法。也许是大家写惯了业务代码,往往对数据结构的知识并不太在意。可不是嘛,只需要玩转QUID就能解决90%以上的问题,还费脑细胞学习基础知识干什么?当然,所有人都会回答“基础知识很重要,数据结构很重要”。然而,当你发现工作几年的开发人员甚至不知道Array和List的区别时,也许会觉得他们当初是在说谎。这是真的,我发现大部分人不知道还有个链表叫“循环链表”。
下面讲述的是多年前的故事。
表格曾是我们在JSP页面常用的布局方式,碰到合并单元格时会不停的使用collspan和rowspan。为了简化操作,我规划了一个布局管理器,用一个字符串代替表格布局。大概长这个样子: layout="3;C2(bop1, bop2),C3(bop3, bop4)" ,layout是自定义标签属性,bop对应布局中的一个细粒度组件,3表示默认3列布局,C2和C3分别表示合并2个和3个单元格。连起来的意思就是,当前容器默认3列布局,其中bop1和bop2需要占两列,bop3和bop4需要占3列,其余组件占1列。还可以进行行合并:layout="3;R2(C2(bop1, bop2)),C3(bop3, bop4)",表示bop1, bop2合并两列后再合并两行。当然,你可以写的更加随意,只要有意义即可,如:layout="3;R2(C2(bop1)),R2(C2(bop2)),C3(bop3, bop4)",layout="4;C2(bop1),C2(bop2),C3(bop3)"。
当时我把这个任务分配给一个工作了4年左右开发人员,并告诉他去看看后缀表达式,并顺便看看解析器模式。
两天后我得到了任务完成的答复,然后开始review。
这下好玩了,布局管理器变成了这样:layout="3;C2:bop1,bop2;C3:bop3;bop4"。似乎没那么坏,至少还能工作,但看起来没那么直观了,似乎也不能更加随意。私自更改设计的原因很简单——这样可以更容易使用split,哈,貌似没有理解如何使用数据结构。
后来使用后缀表达式重写了实现。这实际上是栈结构的典型应用,遍历字符串,根据各种符号为节点,不断入栈出栈。具体过程如下:
1. 刨除前面的默认列数
2. cellMap存储最终解析结果,key对应bop名称,value对应布局,即合并几行几列;stack是表达式栈,存储R1,C1,R2,C2这类xspan表达式;atom是字符串,代表xspan表达式RX,CX或bop
3. 遍历布局管理器字符串
3.1. 遇到数字或字母直接并入atom
3.2 遇到左括号,将atom压入stack,清空atom
3.3 遇到逗号, 如果stack不为空, 将stack中的所有xspan表达式代表的含义赋予bop, 将bop压入cellMap,清空atom; 如果stack为空, 清空atom
3.4 遇到右括号, 将stack中的所有xspan表达式代表的含义赋予bop, 将bop压入cellMap, 从stack弹出一项.
4.布局管理器字符串遍历结束,cellMap是最终结果
以R2(C2(bop1, bop2)),C3(bop3, bop4)为例:
1.遍历字符串,atom="R2"
2.遇到"(",stack.push(atom),atom="", stack栈顶元素为"R2"
3.继续遍历字符串,atom="C2"
4.遇到"(",stack.push(atom),atom="", stack栈顶元素为"C2",第二个元素为"R2"
5.继续遍历字符串,atom="bop1"
6.遇到",",cellMap.put("bop1", "C2,R2"),atom=""
7.继续遍历字符串,atom="bop2"
8.遇到")",cellMap.put("bop2", Cell.C2),stack.pop(), stack中C2出栈,R2升到栈顶
9.遇到")",cellMap.put("bop2", Cell.R2),stack.pop(), stack中R2出栈,stack为空
10.遇到",",此时stack为空,所以atom=""
11.继续遍历字符串,atom="C3"
12.遇到"(",stack.push(atom),atom="", stack栈顶元素为"C3"
13.继续遍历字符串,atom="bop3"
14.遇到",",cellMap.put("bop3", "C3"),atom=""
15.继续遍历字符串,atom="bop4"
16.遇到")",cellMap.put("bop4", "C3"),stack.pop(), stack中C3出栈,stack为空
17.遍历结束,最终得到cellMap
代码示例:
1 /** 2 * 3 * 布局管理器表达式解释器 4 */ 5 public class Interpreter { 6 7 /* 8 * 布局管理器表达式 9 * 布局管理器表达式示例: 10 * 1. 3;C2(bop1, bop2),C3(bop3, bop4); 11 * 默认3列,bop1,bop2合并2列,bop3,bop4合并3列; 12 * 2. 4;R2(C2(bop1, bop2)),C3(bop3, bop4) 13 * 默认4列,bop1,bop2合并2列2行,bop3,bop4合并3列; 14 * 3. 粗粒度组件布局管理器表达式类似: C2(boid1, boid2). 15 */ 16 private String layoutStr; 17 private int columns; //布局的列数 18 /* 19 * key : vcSign, value : formCell 20 * vcSign:如果是细粒度组件,vcSign表示细粒度组件标签的bind属性; 21 * 如果是粗粒度组件,vcSign表示粗粒度组件标签的id属性。 22 */ 23 private Map<String, Cell> cellMap = new HashMap<String, Cell>(); 24 25 private final String EXPRESSION_SPLIT = ";"; 26 private final char FC_SPLIT = ‘,‘; 27 private final char LEFT_BRACKET = ‘(‘; 28 private final char RIGHT_BRACKET = ‘)‘; 29 30 /** 31 * @param layoutStr 布局管理器表达式 32 * @param defColumns 默认列数 33 */ 34 public Interpreter(String layoutStr, int defColumns) { 35 this.layoutStr = StringUtils.removeAllSpace(layoutStr); 36 this.columns = defColumns; 37 } 38 39 /** 40 * 解析布局管理器表达式. 41 */ 42 public void interpret() { 43 if(StringUtils.isEmpty(getLayoutStr())) 44 return; 45 46 String[] layoutSplit = StringUtils.split(getLayoutStr(), EXPRESSION_SPLIT); 47 if(StringUtils.isEmpty(layoutSplit)) 48 return; 49 50 interpertColumns(layoutSplit[0]); 51 for(String str : layoutSplit) { 52 if(StringUtils.isEmpty(str) || MatcherUtil.isNumber(str)) 53 continue; 54 55 interpertFormCell(str); 56 } 57 } 58 59 /** 60 * 解析默认列数 61 * @param str 布局管理器划分的总列数 62 */ 63 private void interpertColumns(String str) { 64 Integer columns = StringUtils.convertToInteger(str); 65 if(columns != null && columns > 0) 66 setColumns(columns); 67 } 68 69 /** 70 * 构造每个bop的布局样式 71 * R2(C2(bop1, bop2)) 或 R2(bop3, C2(bop1, bop2)) 72 * <li>1. 遍历表达式, 解析子元素; 73 * <li>2. 遇到左括号,将R2, C2 表达式压入表达式栈顶; 74 * <li>3. 遇到逗号, 如果栈不为空, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap; 如果栈为空, 直接略过; 75 * <li>4. 遇到右括号, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap, 从表达式栈顶弹出一项. 76 * @param str 77 */ 78 private void interpertFormCell(String str) { 79 //表达式栈, 存储 R1, C1, R2, C2等合并单元格的表达式 80 Stack<String> stack = new Stack<String>(); 81 82 //表达式的元素 83 String atom = ""; 84 //遍历表达式, 解析子元素 85 for(int i = 0, length = str.length(); i < length; i++) { 86 char ch = str.charAt(i); 87 //遇到左括号,将R2, C2 表达式压入表达式栈顶 88 if(LEFT_BRACKET == ch) { 89 stack.push(atom); 90 atom = ""; 91 } 92 //遇到逗号, 如果栈不为空, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap; 93 //如果栈为空, 直接略过; 94 else if(FC_SPLIT == ch) { 95 if(ContainerUtil.isNotNull(stack)) 96 formatCell(atom, stack); 97 atom = ""; 98 } 99 //遇到右括号, 将栈中的所有表达式代表的含义赋予bop, 将bop压入cellMap, 从表达式栈顶弹出一项 100 else if(RIGHT_BRACKET == ch) { 101 if(StringUtils.isNotEmpty(atom)) 102 formatCell(atom, stack); 103 stack.pop();105 } 106 else { 107 atom += ch; 108 } 109 } 110 } 111 112 /** 113 * 将bop压入cellMap 114 * @param vcSign 115 * @param stack 表达式栈 116 */ 117 private void formatCell(String vcSign, Stack<String> stack) { 118 Cell cell = getCell(vcSign); 119 120 Iterator<String> itr = stack.iterator(); 121 while(itr.hasNext()) { 122 String expression = itr.next(); 123 if(isCELLSPAN(expression)) 124 cell.setCollSpan(getCallSpan(expression)); 125 else if(isROWSPAN(expression)) 126 cell.setRowSpan(getRowSpan(expression)); 127 } 128 129 cellMap.put(vcSign, cell); 130 } 131 132 private int getCallSpan(String expression) { 133 int collSpan = Integer.parseInt(expression.toUpperCase().replace("C", "")); 134 return collSpan > getColumns() ? getColumns() : collSpan; 135 } 136 137 private int getRowSpan(String expression) { 138 return Integer.parseInt(expression.toUpperCase().replace("R", "")); 139 } 140 141 private Cell getCell(String atom) { 142 Cell cell = cellMap.get(atom); 143 return cell == null ? new Cell() : cell; 144 } 145 146 private boolean isCELLSPAN(String str) { 147 return MatcherUtil.isAllMatch(str, "^[C|c]-?\\d+$"); 148 } 149 150 private boolean isROWSPAN(String str) { 151 return MatcherUtil.isAllMatch(str, "^[R|r]-?\\d+$"); 152 } 153 154 public int getColumns() { 155 return this.columns; 156 } 157 158 public void setColumns(int columns) { 159 this.columns = columns; 160 } 161 162 public String getLayoutStr() { 163 return layoutStr; 164 } 165 166 public void setLayoutStr(String layoutStr) { 167 this.layoutStr = layoutStr; 168 } 169 170 public Map<String, Cell> getCellMap() { 171 return cellMap; 172 } 173 174 public void setCellMap(Map<String, Cell> cellMap) { 175 this.cellMap = cellMap; 176 }
作者:我是8位的
出处:http://www.cnblogs.com/bigmonkey
本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!