千萬不要再這樣建立集合了!極容易記憶體洩露!

喝水會長肉發表於2021-12-14

由於Java語言的集合框架中(collections, 如list, map, set等)沒有提供任何簡便的語法結構,這使得在建立常量集合時的工作非常繁索。每次建立時我們都要做:

1、定義一個空的集合類變數 2、向這個結合類中逐一新增元素 3、將集合做為引數傳遞給方法

例如,要將一個Set變數傳給一個方法:

Map
<String
,String
> users 
= 
new 
HashMap
<
>
(
) 
{
{

    put ( "Hollis" , "Hollis" ) ;
    put ( "hollis" , "hollis" ) ;
    put ( "HollisChuang" , "HollisChuang" ) ;
} } ;

這樣的寫法稍微有些複雜,有沒有簡潔的方式呢?

雙括號語法初始化集合

其實有一個比較簡潔的方式,那就是 雙括號語法double-brace syntax)建立並初始化一個新的集合:

javac DoubleBraceTest.java

同理,建立並初始化一個HashMap的語法如下:

DoubleBraceTest
.class

DoubleBraceTest$ 1.class

不只是Set、Map,jdk中的集合類都可以用這種方式建立並初始化。

當我們使用這種雙括號語法初始化集合類的時候,在對Java檔案進行編譯時,可以發現一個奇怪的現象,使用javac對DoubleBraceTest進行編譯:

javac DoubleBraceTest.java

我們會發現,得到兩個class檔案:

Map hollis 
= 
new 
HashMap
(
)
{
{

    put ( "firstName" , "Hollis" ) ;
    put ( "lastName" , "Chuang" ) ;
    put ( "contacts" , new HashMap ( ) { {
        put ( "0" , new HashMap ( ) { {
            put ( "blogs" , " ) ;
        } } ) ;
        put ( "1" , new HashMap ( ) { {
            put ( "wechat" , "hollischuang" ) ;
        } } ) ;
    } } ) ;
} } ;

有經驗的朋友可能一看到這兩個檔案就會知道,這裡面一定用到了匿名內部類。

沒錯,使用這個雙括號初始化的效果是建立匿名內部類。建立的類有一個隱式的this指標指向外部類。

不建議使用這種形式

首先,使用這種形式建立並初始化集合會導致很多內部類被建立。因為每次使用雙大括號初始化時,都會生成一個新類。如這個例子:

DoubleBraceTest$
1$
1$
1.class

DoubleBraceTest$ 1$ 1$ 2.class
DoubleBraceTest$ 1$ 1.class
DoubleBraceTest$ 1.class
DoubleBraceTest .class

這會使得很多內部類被建立出來:


public Map 
getMap
(
) 
{

   Map hollis = new HashMap ( ) { {
        put ( "firstName" , "Hollis" ) ;
        put ( "lastName" , "Chuang" ) ;
        put ( "contacts" , new HashMap ( ) { {
            put ( "0" , new HashMap ( ) { {
                put ( "blogs" , " ) ;
            } } ) ;
            put ( "1" , new HashMap ( ) { {
                put ( "wechat" , "hollischuang" ) ;
            } } ) ;
        } } ) ;
    } } ;

    return hollis ;
}

這些內部類被建立出來,是需要被類載入器載入的,這就帶來了一些額外的開銷。

如果您使用上面的程式碼在一個方法中建立並初始化一個map,並從方法返回該map,那麼該方法的呼叫者可能會毫不知情地持有一個無法進行垃圾收集的資源。


public 
class 
DoubleBraceTest 
{

    public static void main ( String [ ] args ) {
       DoubleBraceTest doubleBraceTest = new DoubleBraceTest ( ) ;
       Map map = doubleBraceTest . getMap ( ) ;
    }
}

我們嘗試通過呼叫getMap得到這樣一個通過雙括號初始化出來的map

Field field 
= map
.
getClass
(
)
.
getDeclaredField
(
"this$0"
)
;

field . setAccessible ( true ) ;
System .out . println (field . get (map ) . getClass ( ) ) ;

返回的Map現在將包含一個對DoubleBraceTest的例項的引用。讀者可以嘗試這通過debug或者以下方式確認這一事實。

List<String> list2 = Arrays.asList("hollis ", "Hollis", "HollisChuang");

替代方案

很多人使用雙括號初始化集合,主要是因為他比較方便,可以在定義集合的同時對他進行初始化。

但其實,目前已經有很多方案可以做這個事情了,不需要再使用這種存在風險的方案。

使用Arrays工具類

當我們想要初始化一個List的時候,可以藉助Arrays類,Arrays中提供了asList可以把一個陣列轉換成List:

List<String> list1 = Stream.of("hollis", "Hollis", "HollisChuang").collect(Collectors.toList());

但是需要注意的是,asList 得到的只是一個 Arrays 的內部類,是一個原來陣列的檢視 List,因此如果對它進行增刪操作會報錯。

使用Stream

Stream是Java中提供的新特性,他可以對傳入流內部的元素進行篩選、排序、聚合等中間操作(intermediate operate),最後由最終操作(terminal operation)得到前面處理的結果。

我們可以藉助Stream來初始化集合:

ImmutableMap
.
of
(
"k1"
, 
"v1"
, 
"k2"
, 
"v2"
)
;

ImmutableList . of ( "a" , "b" , "c" , "d" ) ;

使用第三方工具類

很多第三方的集合工具類可以實現這個功能,如Guava等:

ImmutableMap
.
of
(
"k1"
, 
"v1"
, 
"k2"
, 
"v2"
)
;

ImmutableList . of ( "a" , "b" , "c" , "d" ) ;

關於Guava和其中定義的不可變集合,我們在後面會詳細介紹

Java 9內建方法

其實在Java 9 中,在List、Map等集合類中已經內建了初始化的方法,如List中包含了12個過載的of方法,就是來做這個事情的:


/**

* Returns an unmodifiable list containing zero elements.
* java學習交流:737251827  進入可領取學習資源及對十年開發經驗大佬提問,免費解答!
* See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
*
* @param <E> the {@code List}'s element type
* @return an empty {@code List}
*
* @since 9
*/

static < E > List < E > of ( ) {
    return ImmutableCollections . emptyList ( ) ;
}

static < E > List < E > of ( E e1 ) {
    return new ImmutableCollections .List12 < > (e1 ) ;
}

static < E > List < E > of ( E ... elements ) {
    switch ( elements .length ) { // implicit null check of elements
        case 0 :
            return ImmutableCollections . emptyList ( ) ;
        case 1 :
            return new ImmutableCollections .List12 < > (elements [ 0 ] ) ;
        case 2 :
            return new ImmutableCollections .List12 < > (elements [ 0 ] , elements [ 1 ] ) ;
        default :
            return new ImmutableCollections .ListN < > (elements ) ;
    }
}




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70010294/viewspace-2847637/,如需轉載,請註明出處,否則將追究法律責任。

相關文章