網頁

2019/2/26

Design Pattern Java Integer Flyweight Pattern 享元模式

今天在學習享元模式 Flyweight Pattern,發現原來Java的Integer中有用到這個模式。

為了避免以下內容讓你混淆,先記得比較任意兩個Integer物件時,請使用equals()方法,不該用==


例如下面程式碼,使用Integer.valueOf(int i)來取得Integer物件。

Integer integer1 = Integer.valueOf(1);
Integer integer2 = Integer.valueOf(1);
        
System.out.println(integer1 == integer2); // true
        
        
Integer integer3 = Integer.valueOf(128);
Integer integer4 = Integer.valueOf(128);
        
System.out.println(integer3 == integer4); // false

真是太神奇了,為什麼上下的結果不同?但看了Integer.valueOf()的原始碼才發現原來其例用享元模式維護了一個靜態的Integer cache[]的物件池來達到重用的目的。

public final class Integer extends Number implements Comparable<Integer> {
    ...
    private static class IntegerCache {
        static final int low = -128; // cache[] 預設的最低範圍
        static final int high; // cache[] 的最高範圍
        static final Integer cache[]; // <-- 此用來存放重用的Integer物件

        static {
            // high value may be configured by property
            int h = 127; // cache[] 預設的最高範圍值,可透過VM參數XX:AutoBoxCacheMax調整
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
    
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high) // 判斷cache[]中是否已存在要取得的Integer物件
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    ...
}

Integer初始化的時候有個內部靜態類別IntegerCache,其靜態成員變數cache[]預設會生成-128到127Integer物件儲存起來。如果呼叫Integer.valueOf()來取得Integer物件時,若輸入的參數值落在-128至127的範圍內就會從這個cache[]取得。

上面範例的integer1integer2輸入參數值(1)落在範圍內,因此皆從cache[]中返回同一個Integer物件,所以用==比較參照位址結果為true。

integer3integer4的輸入值(128)落在範圍外,所以各自返回一個新的Integer物件,所以用==比較時,因為所指的參照位址不同,所以結果為false。

也就是說,在Java程式中如果要取得Integer物件,建議用Integer.valueOf()來取得,不要用new的方式,有較好的效能且可減少記憶體的消耗。

引述API說明

Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

cache[]的最大值可透過啟動Java程式時輸入系統參數-Djava.lang.Integer.IntegerCache.highXX:AutoBoxCacheMax來調整。

但總之再強調一次,別使用==來比較任何物件。


參考:

沒有留言:

張貼留言