for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ){ Suit suit = i.next(); for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) deck.add(new Card(suit, j.next())); }
虽然解决了问题,但是很丑,更简洁的写法如下
1 2 3
for (Suit suit : suits) for (Rank rank : ranks) deck.add(new Card(suit, rank));
尽可能的使用增强for循环 #####Item 47: Know and use the libraries 不要重复造轮子 如果需要一个常用的功能,去发现一个库并使用它,往往比自己写的要好 因为库会随着时间推移更新迭代越来越好,自己也节约了时间 这并不会评论一个人的能力 #####Item 48: Avoid float and double if exact answers are required 当需要一个精确的答案时,避免使用float或double类型的变量
float或double是为了科学和工程计算而设计的,特别不适用于货币计算
推荐使用int或long代替 #####Item 49: Prefer primitive types to boxed primitives
使用原始类型替代装箱类型
byte short char int long float double boolbean等是Java中的基本类型,也有对应的装箱类型,如 Integer, Double, and Boolean.应当谨慎对待这两者之间的差别
首先第一个差别,原始类型仅仅包含对应的值,装箱类型既包含对应的值也有对应的引用,第二个差别是装箱类型有可能为null,第三个差别是原始类型在时间和空间消耗中更高效 #####Item 50: Avoid strings where other types are more appropriate
#####Item 51: Beware the performance of string concatenation
考虑字符连接(+)的性能
使用(+)能很方便的拼接若干个字符串,但是我们也要考虑到开销
如下两个代码都是拼接字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
//方式一 public String statement(){ String result = ""; for (int i = 0; i < numItems(); i++) result += lineForItem(i); // String concatenation return result; }
//方式二 public String statement(){ StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH); for (int i = 0; i < numItems(); i++) b.append(lineForItem(i)); return b.toString(); }
More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason—including blind stupidity. —William A. Wulf [Wulf72] 更多的计算罪恶是以效率的名义犯下的(不一定要达到这一目的),而不是因为任何其他单一的原因-包括盲目的愚蠢
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. —Donald E. Knuth [Knuth74] 我们应该忽略小效率,大约百分之97的情况下,邪恶之源就是过早优化
We follow two rules in the matter of optimization: Rule 1. Don’t do it. Rule 2 (for experts only). Don’t do it yet—that is, not until you have a perfectly clear and unoptimized solution. —M. A. Jackson [Jackson75] 关于优化遵循如下两个原则: 原则1.别这样做 原则2.(仅对专家而言).还不要做,直到你找到一个清晰的解决方案或者一个未优化的解决办法
// bad Tagged class - vastly inferior to a class hierarchy! classFigure{ enumShape{ RECTANGLE, CIRCLE }; // Tag field - the shape of this figure final Shape shape; // These fields are used only if shape is RECTANGLE double length; double width; // This field is used only if shape is CIRCLE double radius; // Constructor for circle Figure(double radius) { shape = Shape.CIRCLE; this.radius = radius; } // Constructor for rectangle Figure(double length, double width) { shape = Shape.RECTANGLE; this.length = length; this.width = width; } doublearea(){ switch(shape) { case RECTANGLE: return length * width; case CIRCLE: return Math.PI * (radius * radius); default: thrownew AssertionError(); } } }
优化如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//Class hierarchy replacement for a tagged class abstractclassFigure{ abstractdoublearea(); } classCircleextendsFigure{ finaldouble radius; Circle(double radius) { this.radius = radius; } doublearea(){ return Math.PI * (radius * radius); } }
Item32: Use EnumSet instead of bit fields(用EnumSet替代位字段)
如我们有一段代码
1 2 3 4 5 6 7 8 9 10 11
// Bit field enumeration constants - OBSOLETE! publicclassText{ publicstaticfinalint STYLE_BOLD = 1 << 0; // 1 publicstaticfinalint STYLE_ITALIC = 1 << 1; // 2 publicstaticfinalint STYLE_UNDERLINE = 1 << 2; // 4 publicstaticfinalint STYLE_STRIKETHROUGH = 1 << 3; // 8 // Parameter is bitwise OR of zero or more STYLE_ constants publicvoidapplyStyles(int styles){ ... } } //使用 text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
用EnumSet优化如下:
1 2 3 4 5 6 7 8 9
// EnumSet - a modern replacement for bit fields publicclassText{ publicenumStyle{ BOLD, ITALIC, UNDERLINE, STRIKETHROUGH } // Any Set could be passed in, but EnumSet is clearly best publicvoidapplyStyles(Set<Style> styles){ ... } }
/** * Returns a BigInteger whose value is (this mod m). This method * differs from the remainder method in that it always returns a * non-negative BigInteger. * * @param m the modulus, which must be positive * @return this mod m * @throws ArithmeticException if m is less than or equal to 0 */ public BigInteger mod(BigInteger m){ if (m.signum() <= 0) thrownew ArithmeticException("Modulus <= 0: " + m); ... // Do the computation }
对于私有的方法则使用断言
1 2 3 4 5 6 7
// Private helper function for a recursive sort privatestaticvoidsort(long a[], int offset, int length){ assert a != null; assert offset >= 0 && offset <= a.length; assert length >= 0 && length <= a.length - offset; ... // Do the computation }
//一个不可变的周期类 // Broken "immutable" time period class publicfinalclassPeriod{ privatefinal Date start; privatefinal Date end; /** * @param start the beginning of the period * @param end the end of the period; must not precede start * @throws IllegalArgumentException if start is after end * @throws NullPointerException if start or end is null */ publicPeriod(Date start, Date end){ if (start.compareTo(end) > 0) thrownew IllegalArgumentException( start + " after " + end); this.start = start; this.end = end; } public Date start(){ return start; }
public Date end(){ return end; } ... // Remainder omitted }
其中Date对象是可变的
1 2 3 4 5
// bad Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78); // 修改了p的内部状态
为了保护对象的状态,我们需要在构造函数中对可变参数执行拷贝防御
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Repaired constructor - makes defensive copies of parameters publicPeriod(Date start, Date end){ this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); if (this.start.compareTo(this.end) > 0) thrownew IllegalArgumentException(start +" after "+ end); }
// Repaired accessors - make defensive copies of internal fields public Date start(){ returnnew Date(start.getTime()); } public Date end(){ returnnew Date(end.getTime()); }
staticintsum(int... args){ int sum = 0; for (int arg : args) sum += arg; return sum; }
sum(1,2,3) –> 6 sum() –> 0
暂略 #####Item 43: Return empty arrays or collections, not nulls 像如下的代码很常见
1 2 3 4 5 6 7 8 9 10 11
//bad privatefinal List<Cheese> cheesesInStock = ...; /** * @return an array containing all of the cheeses in the shop, * or null if no cheeses are available for purchase. */ public Cheese[] getCheeses() { if (cheesesInStock.size() == 0) returnnull; ... }
// The right way to return an array from a collection privatefinal List<Cheese> cheesesInStock = ...; privatestaticfinal Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0]; /** * @return an array containing all of the cheeses in the shop. */ public Cheese[] getCheeses() { if(cheesesInStock.size() <= 0) return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY); }
//or
public List<Cheese> getCheeseList(){ if (cheesesInStock.isEmpty()) return Collections.emptyList(); // Always returns same list else returnnew ArrayList<Cheese>(cheesesInStock); }
总之,使用集合或数组的任何情况下都不能返回null
Item 44: Write doc comments for all exposed API elements
publicclassStack<E> { publicStack(); publicvoidpush(E e); public E pop(); publicbooleanisEmpty(); } //push所有元素 publicvoidpushAll(Iterable<E> src){ for (E e : src) push(e); }