2017-01-05 47 views
4

我需要創建一個約300個值的枚舉,並有能力通過id(int)獲取其值。目前,我有這樣的:通過數字編號獲取枚舉的最佳方法

public enum Country { 
    DE(1), US(2), UK(3); 

    private int id; 
    private static Map<Integer, Country> idToCountry = new HashMap<>(); 
    static { 
     for (Country c : Country.values()) { 
      idToCountry.put(c.id, c); 
     } 
    } 

    Country(int id) { 
     this.id = id; 
    } 

    public static Country getById(int id) { 
     return idToCountry.get(id); 
    } 
} 

該枚舉將被用到了很多,所以我不知道這是否是最佳的解決方案的性能,明智的。

我在讀http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html一遍又一遍,也沒有找到一個描述在什麼時候

static { 

} 

塊被稱爲部分,如果guaranted,這將是一次叫。那麼 - 是嗎?

+2

靜態初始化塊被調用一次[當類被初始化時](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.7)。不保證被調用一次,但除非你使用類加載器做了一些奇特的事情。 –

+0

完美,謝謝! @AndyTurner你會推薦另一種初始化方式嗎,還是我應該這樣做? – Michael

+1

這看起來很好。我唯一要做的就是讓'id'' final'。 (還有'idToCountry'' final')。另一種方法是將它們放在一個數組中(假設你的ID是非負數且合理連續的) - 這可以讓你避免自動裝入'id'。 –

回答

3

靜態初始化塊被調用一次when the class is initialized。不保證被調用一次,但除非你使用類加載器做了一些奇特的事情。

因此,您的方法是可能罰款從性能的角度來看。我建議的唯一改變是讓你的領域final


的另一種方式來表示所述映射可以是存儲元件的陣列(或列表):

System.out.println(countries[1]); // DE. 

Country[] countries = new Countries[maxId + 1]; 
for (Country country : Country.values()) { 
    countries[country.id] = country; 
} 

然後,您可以通過元素索引查找它們

這樣可以避免爲了打電話給idToCountry.get(Integer)而必須裝箱id的性能損失。

這當然要求您具有非負面的ID(理想情況下ID應合理連續,以避免必須在國家之間存儲大量null)。

+0

是的,我會與替代,偉大的提示! – Michael

+0

@Michael。請注意,您真的需要爲HashMap.get()進行大量查找(Integer的哈希碼計算不重要,因爲它只是返回整數值),因此這裏是您的性能問題。雖然hashmap更加清晰,但出錯的機率較低(例如需要對請求的ID進行範圍檢查)。 – Thirler

+1

@邁克爾我同意蒂勒。首先嚐試使用HashMap,只有在發現仍然存在性能問題時才使用該數組。 –

4

在情況下,如果第一個國家id爲0和ID由1遞增,您可以使用另一個方法:

  1. 在陣列緩存枚舉值。 Enum.values()按照它們在枚舉中聲明的順序返回元素。但它應該被緩存,因爲每次調用它時都會創建新的數組。
  2. 通過id從緩存數組中獲取值,這將是數組索引。

請參見下面的代碼:

enum Country { 
    A, B, C, D, E; 
    private static final Country[] values = Country.values(); 

    public static Country getById(int id) { 
     return values[id]; 
    } 
} 

UPDATE:要得到國家的ID,應使用ordinal()方法。而爲了讓越來越ID代碼更清晰,下一個方法可以添加到枚舉:

public int getId() { 
    return ordinal(); 
} 
1

首先,你不需要有一個靜態塊創建地圖。您可以將代碼添加到構造函數中,其中每個組件都將其自身添加到您的地圖中。枚舉總是一個sigleton,所以你的構造函數保證只被調用一次(每個枚舉值)你也不需要甚至有ID,因爲Enum的方法public final int ordinal()返回枚舉中從零開始的連續數字。在你的情況下,DE的分數爲0,美國的分數爲1,英國的分數爲2。

下面是一個例子:

public enum Country { 
DE, US, UK; 

private static Map<Integer, Country> idToCountry = new HashMap<>(); 

    Country() { 
     idToCountry.put(this.ordinal(), this); 
    } 

    public static Country getById(int id) { 
     return idToCountry.get(id); 
    } 
} 
+1

對於此代碼,我得到編譯器錯誤 '無法在'idCountry.put(...)'行中的初始化程序中引用靜態枚舉字段Country.idToCountry「。 –

0

你可以試試這一個了。簡單如它所示。

enum Country { 
    DE(1), US(2), UK(3); 

    public int id; 

    Country(int id) { 
    this.id = id; 
} 

public static Country getCountry(int id) { 
    Country[] c = new Country[Country.values().length]; 
    c = Country.values(); 
    return c[id]; 
    } 
} 

非常感謝。