api
本文主要介绍的两个类:stringgenerator和objectiterator。
字符串生成器
stringgenerator工具类将对象转化为字符串,使对象可读性更好。可以用它来实现类的tostring方法或者把对象的字符串表达作为日志调试代码:
package com.stackhunter.util.tostring.example; import com.stackhunter.example.employee.department; import com.stackhunter.example.employee.employee; import com.stackhunter.example.employee.manager; import com.stackhunter.example.people.person; import com.stackhunter.util.tostring.stringgenerator; public class stringgeneratorexample { public static void main(string[] args) { department department = new department(5775, "sales") .setemployees( new employee(111, "bill", "gates"), new employee(222, "howard", "schultz"), new manager(333, "jeff", "bezos", 75000)); system.out.println(stringgenerator.generate(department)); system.out.println(stringgenerator.generate(new int[] { 111, 222, 333 })); system.out.println(stringgenerator.generate(true)); } }
stringgenerator.generate()将department,数组和boolean值进行格式化输出。
com.stackhunter.example.employee.department@129719f4 deptid = 5775 employeelist = java.util.arraylist@7037717a employeelist[0] = com.stackhunter.example.employee.employee@17a323c0 firstname = bill id = 111 lastname = gates employeelist[1] = com.stackhunter.example.employee.employee@57801e5f firstname = howard id = 222 lastname = schultz employeelist[2] = com.stackhunter.example.employee.manager@1c4a1bda budget = 75000.0 firstname = jeff id = 333 lastname = bezos name = sales [i@39df3255 object[0] = 111 object[1] = 222 object[2] = 333 true
对象迭代器
objectiterator使用迭代器模式遍历对象的属性,以键值对形式保存。对象中的java bean、集合、数组及map都要进行迭代。objectiterator也会考虑到对象之间循环引用的处理。
package com.stackhunter.util.tostring.example; import com.stackhunter.example.employee.department; import com.stackhunter.example.employee.employee; import com.stackhunter.example.employee.manager; import com.stackhunter.util.objectiterator.objectiterator; public class objectiteratorexample { public static void main(string[] args) { department department = new department(5775, "sales") .setemployees( new employee(111, "bill", "gates"), new employee(222, "howard", "schultz"), new manager(333, "jeff", "bezos", 75000)); objectiterator iterator = new objectiterator("some department", department); while (iterator.next()) { system.out.println(iterator.getname() + "=" + iterator.getvalueasstring()); } } }
通过遍历整个对象生成键值对的集合。使用getvalueasstring()方法而不是tostring()格式化输出。对于原始类型、包装类型、字符串、日期和枚举使用原始的tostring()实现。对于其他类型输出类名和hash值。
objectiterator.getdepth()会增加缩进,输出更易读。调用next()之前使用nextparent()缩短当前分支跳跃到下一属性。
some department=com.stackhunter.example.employee.department@780324ff deptid=5775 employeelist=java.util.arraylist@6bd15108 employeelist[0]=com.stackhunter.example.employee.employee@22a79c31 firstname=bill ...
java对象迭代器的具体实现
实现iterator模式的第一步是创建通用的迭代器接口:iobjectiterator。无论遍历的对象是java bean、数组还是map都可以使用该接口。
public interface iobjectiterator { boolean next(); string getname(); object getvalue(); }
使用该接口可以按照单一顺序依次获取当前属性的name和value。
实现了iobjectiterator的类用来处理某一种类型的对象。大多数类调用getname()返回名称前缀。arrayiterator使用了元素的索引:return name + "[" + nextindex + "]";。
属性迭代器
propertyiterator可能是最重要的迭代类。它使用java bean introspection读取对象属性,将它们转化为键值对序列。
public class propertyiterator implements iobjectiterator { private final object object; private final propertydescriptor[] properties; private int nextindex = -1; private propertydescriptor currentproperty; public propertyiterator(object object) { this.object = object; try { beaninfo beaninfo = introspector.getbeaninfo(object.getclass()); properties = beaninfo.getpropertydescriptors(); } catch (runtimeexception e) { throw e; } catch (exception e) { throw new runtimeexception(e.getmessage(), e); } } @override public boolean next() { if (nextindex + 1 >= properties.length) { return false; } nextindex++; currentproperty = properties[nextindex]; if (currentproperty.getreadmethod() == null || "class".equals(currentproperty.getname())) { return next(); } return true; } @override public string getname() { if (currentproperty == null) { return null; } return currentproperty.getname(); } @override public object getvalue() { try { if (currentproperty == null) { return null; } return currentproperty.getreadmethod().invoke(object); } catch (runtimeexception e) { throw e; } catch (exception e) { throw new runtimeexception(e.getmessage(), e); } } }
数组迭代器
arrayiterator通过反射得到数组的长度,进而检索每个数据元素。arrayiterator不关心从getvalue()方法返回值的具体细节。它们一般情况下被传递给propertyiterator。
public class arrayiterator implements iobjectiterator { private final string name; private final object array; private final int length; private int nextindex = -1; private object currentelement; public arrayiterator(string name, object array) { this.name = name; this.array = array; this.length = array.getlength(array); } @override public boolean next() { if (nextindex + 1 >= length) { return false; } nextindex++; currentelement = array.get(array, nextindex); return true; } @override public string getname() { return name + "[" + nextindex + "]"; } @override public object getvalue() { return currentelement; } }
集合迭代器
collectioniterator与arrayiterator非常相似。使用java.lang.iterable调用它的iterable.iterator()方法初始化内部迭代器。
map迭代器
mapiterator遍历java.util.map的entry。它并不深入到每个entry的键值对,这个工作由mapentryiterator类完成。
public class mapiterator implements iobjectiterator { private final string name; private iterator<?> entryiterator; private map.entry<?, ?> currententry; private int nextindex = -1; public mapiterator(string name, map<?, ?> map) { this.name = name; this.entryiterator = map.entryset().iterator(); } @override public boolean next() { if (entryiterator.hasnext()) { nextindex++; currententry = (entry<?, ?>) entryiterator.next(); return true; } return false; } ... }
map entry迭代器
mapentryiterator处理java.util.map的单个entry。它只返回两个值:entry的键和值。与arrayiterator及其他的类似,如果是复杂类型的话,它的结果可能最终传递给propertyiterator,作为java bean处理。
根迭代器
rootiterator返回单个元素——初始节点。可以把它想成xml文件的根节点。目的是发起整个遍历过程。
整合
objectiterator类作为门面角色(facade),包装了所有的遍历逻辑。它根据最后一次getvalue()的返回值类型决定哪个iobjectiterator的子类需要实例化。当子迭代器在内部创建时它在栈中保存当前迭代器的状态。它也暴露了getchild()和getdepth()方法为调用者展示当前进度。
private iobjectiterator iteratorfor(object object) { try { if (object == null) { return null; } if (object.getclass().isarray()) { return new arrayiterator(name, object); } if (object instanceof iterable) { return new collectioniterator(name, (iterable<?>) object); } if (object instanceof map) { return new mapiterator(name, (map<?, ?>) object); } if (object instanceof map.entry) { return new mapentryiterator(name, (map.entry<?, ?>) object); } if (issinglevalued(object)) { return null; } return new propertyiterator(object); } catch (runtimeexception e) { throw e; } catch (exception e) { throw new runtimeexception(e.getmessage(), e); } }
字符串生成器的实现
已经看到如何遍历对象中的所有属性。最后的工作就是让输出更加美观,以及增加一些限制条件(比如不能创建一个gb级大小的字符串)。
public static string generate(object object) { string s = ""; objectiterator iterator = new objectiterator("object", object); ... while (iterator.next()) { if (s.length() >= max_string_length) { return s; } if (iterator.getchild() >= max_children) { iterator.nextparent(); continue; } string valueasstring = iterator.getvalueasstring(); s += system.lineseparator(); s += indent(iterator.getdepth()) + truncatestring(iterator.getname()); if (valueasstring == null) { s += " = null"; } else { s += " = " + truncatestring(valueasstring); } } return s; }
代码第21行完成了格式化,增加了缩进和层次结构使显示更美观。
同时增加了一些限制条件:
第9行——限制字符串长度在16k内。
第13行——限制任何父节点下子节点数量小于64.
第21&25行——限制键和值长度在64个字符。
总结
本文介绍了如何使用迭代器模式遍历包含各种复杂属性的对象。关键是将每种类型的迭代委派给各自的类实现。可以在你自己的软件中使用这两个工具。