代码改变世界

如何避免创建不必要的对象

2017-03-05 22:31  ttylinux  阅读(460)  评论(0编辑  收藏  举报
本文涉及到的概念
 
1.在保证程序清晰性和简洁性的基础下,重用具有相同功能的对象,避免创建新的对象
2.适配器对象,adapter Object
3.自动装箱,基本数据类型和装箱类型
 
 
在保证程序清晰性和简洁性的基础下,重用具有相同功能的对象,避免创建新的对象
当该对象的状态是不变化的,新创建的对象具有的功能与原来对象相同的,那么就避免创建新的对象,直接使用原来的对象。如果代码中发现这种情况,就做出调整,调整为使用一个对象,避免使用新的对象。
例子:来自<<effective Java>>
方法isBabyBoomer,用来检查一个Person实例,该实例是不是出生在婴儿期(1946年1月到1965年1月)。Person类的每个实例,每次调用该方法,都会创建相同的boomStart对象,boomEnd对象。一次调用,创建如下对象,一个Calendar,一个TimeZone,两个Date对象。当然,它们都是局部变量,每次方法调用完后,就被销毁。
可以提高一下性能,修改为:
因为boomStart和boomEnd在所有实例中功能相同,而且还没有被修改,那么,就可以提出来,让它成为静态变量。为所有实例共用。

 

适配器对象,adapter Object
上面Person提取的共用的对象,它们的状态在实例化后就不会被修改。而适配器对象,它的状态会被修改。
调用Map实例的keySet方法,它每次返回的都是同一个对象。不同的线程,持有该Map对象,调用keySet方法,得到的都是同一个keySet对象。对该keySet对象的状态进行修改,要在多个线程中可见,就需要使用volatile来修饰。
Map:
 
public interface Map<K,V> {  
  // Views
 
  /**
  * Returns a {@link Set} view of the keys contained in this map.
  * The set is backed by the map, so changes to the map are
  * reflected in the set, and vice-versa. If the map is modified
  * while an iteration over the set is in progress (except through
  * the iterator's own <tt>remove</tt> operation), the results of
  * the iteration are undefined. The set supports element removal,
  * which removes the corresponding mapping from the map, via the
  * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
  * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
  * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
  * operations.
  *
  * @return a set view of the keys contained in this map
  */
  Set<K> keySet();
...
 }
 
// Modification Operations
 
  /**
  * Associates the specified value with the specified key in this map
  * (optional operation). If the map previously contained a mapping for
  * the key, the old value is replaced by the specified value. (A map
  * <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
  * if {@link #containsKey(Object) m.containsKey(k)} would return
  * <tt>true</tt>.)
  *
  * @param key key with which the specified value is to be associated
  * @param value value to be associated with the specified key
  * @return the previous value associated with <tt>key</tt>, or
  * <tt>null</tt> if there was no mapping for <tt>key</tt>.
  * (A <tt>null</tt> return can also indicate that the map
  * previously associated <tt>null</tt> with <tt>key</tt>,
  * if the implementation supports <tt>null</tt> values.)
  * @throws UnsupportedOperationException if the <tt>put</tt> operation
  * is not supported by this map
  * @throws ClassCastException if the class of the specified key or value
  * prevents it from being stored in this map
  * @throws NullPointerException if the specified key or value is null
  * and this map does not permit null keys or values
  * @throws IllegalArgumentException if some property of the specified key
  * or value prevents it from being stored in this map
  */
  V put(K key, V value);
 
AbstractMap:
 
  // Views
 
  /**
  * Each of these fields are initialized to contain an instance of the
  * appropriate view the first time this view is requested. The views are
  * stateless, so there's no reason to create more than one of each.
  */
  transient volatile Set<K> keySet = null;
  transient volatile Collection<V> values = null;
 
 
HashMap继承AbstractMap:
entrySet不会被序列化
  // Views
  private transient Set<Map.Entry<K,V>> entrySet = null;
  /**
  * Returns a {@link Set} view of the keys contained in this map.
  * The set is backed by the map, so changes to the map are
  * reflected in the set, and vice-versa. If the map is modified
  * while an iteration over the set is in progress (except through
  * the iterator's own <tt>remove</tt> operation), the results of
  * the iteration are undefined. The set supports element removal,
  * which removes the corresponding mapping from the map, via the
  * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
  * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
  * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
  * operations.
  */
  public Set<K> keySet() {
  Set<K> ks = keySet;
  return (ks != null ? ks : (keySet = new KeySet()));
  }
  private final class KeySet extends AbstractSet<K> {
  public Iterator<K> iterator() {
  return newKeyIterator();
  }
  public int size() {
  return size;
  }
  public boolean contains(Object o) {
  return containsKey(o);
  }
  public boolean remove(Object o) {
  return HashMap.this.removeEntryForKey(o) != null;
  }
  public void clear() {
  HashMap.this.clear();
  }
  }
 

 

 
验证程序:验证keySet方法返回的对象是不是同一个对象
 
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
 
 
public class MapExample {
 
public static void main(String[] args){
 
Map<String,String> one = new HashMap<String,String>();
one.put("Key1", "one");
one.put("key2", "two");
 
 
Set<String> keySet = one.keySet();
Iterator<String> iterator = keySet.iterator();
while(iterator.hasNext()){
 
System.out.println(iterator.next());
 
}
 
one.put("Key3", "three");
one.put("key4", "four");
Set<String> keySet2 = one.keySet();
if(keySet2 == keySet){
System.out.println("They are same");
}
 
Iterator<String> iterator2 = keySet2.iterator();
while(iterator2.hasNext()){
 
System.out.println(iterator2.next());
}
}
 
}
 
输出结果:
Key1
key2
They are same
Key1
key2
Key3
key4

 

 
 
自动装箱,基本数据类型和装箱类型
 
一个例子
 
public class AutoBoxingExam {
 
public static void main(String[] args){
 
Long sum = 0l;
for(long i = 0; i < Integer.MAX_VALUE; i++){
 
sum = sum + i; 
// sum is an Object, can not use +
// so auto unboxing
//sum.longValue() + i
//then sum = new Long(sum.longValue()+i);
//sum = new Long(sum.longValue()+i);
}
}
 
}

 

 
所以整个过程运行下来,在执行过程中,创建了多余的Integer.MAX_VALUE个Long对象。Integer的最大值是2的31次方,32位可以用来表达最大的数字是2的31次方,其中1位用来做符号位
 
使用自动装箱时,要注意它有可能创建新的对象。
 
引用: