避免创建不必要的对象--EffectiveJava小结(5)

2018-08-01  本文已影响0人  冰鱼飞鸟

尽量重用对象,而不是重新创建。

一.重用不可变对象

不可变对象,始终可以被重用。
如String

//尽量用,多次调用不会重复创建对象
String s = "";
//而不是,调多少次就创建多少次对象
String s2 = new String("");

另外调用静态工厂方法优于调用构造器。
如Boolean.valueOf(String); 优于new Boolean(String)
因为前者会返回Boolean中缓存的true和false对象,避免重复创建。

二.重用适配器/视图对象

适配器/视图对象是指这样一个对象:它把功能委托给一个后备对象,从而为后备对象提供一个可以替代的接口。
由于其除了后备对象外没有其他状态信息,所以针对某个给定对象的特定适配器而言,它不需要创建多个适配器实例。

这句话咋一看难以理解,没有代入感。下面以HashMap的keySet为例进行理解

final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }
    
    final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }
    
    final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }

这个KeySet类就是一个适配器/视图。

根据视图这个名字我们就可以初步理解为KeySet这个类本身并没有存储数据,其数据来源都是其后备对象即HashMap。

可以看到KeySet只有方法没有自己的属性,并且方法中使用的都是HashMap中的属性。程序调用KeySet的方法实际是访问了这个HashMap的方法或属性。

这样看来如果为同一个HashMap创建多个KeySet对象并没有意义。因为修改了任何一个KeySet其实都是修改的这个HashMap。完全没有必要创建用处完全相同的多个KeySet。

至此把上面那句话用HashMap,KeySet代入来说的话就是

HashMap中的KeySet对象把KeySet的功能委托给这个HashMap对象来完成,从而为HashMap提供一个可以替代的接口。由于这个KeySet除了HashMap这个后备对象外,没有其他状态信息,所以针对一个给定的HashMap的KeySet而言,不需要创建多个KeySet实例。

三.注意自动装箱导致的创建多余对象

四.不需要过于追求性能而尽可能不创建对象。
实际上小对象的创建和回收代价很小,如果创建新的对象可以增加程序的清晰性,简洁性和功能性,那通常是件好事。
如为了保护原有数据而拷贝对象导致的重复创建。这时候重用对象可能会付出更大的代价。

上一篇下一篇

猜你喜欢

热点阅读