千萬不要再這樣建立集合了!極容易記憶體洩露!
由於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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- SHBrowseForFolder 記憶體洩露記憶體洩露
- 記憶體溢位和記憶體洩露記憶體溢位記憶體洩露
- Lowmemorykiller記憶體洩露分析記憶體洩露
- 使用 mtrace 分析 “記憶體洩露”記憶體洩露
- 實戰Go記憶體洩露Go記憶體洩露
- Android 記憶體洩露詳解Android記憶體洩露
- ArkTS 的記憶體快照與記憶體洩露除錯記憶體洩露除錯
- Linux記憶體洩露案例分析和記憶體管理分享Linux記憶體洩露
- Pprof定位Go程式記憶體洩露Go記憶體洩露
- nodejs爬蟲記憶體洩露排查NodeJS爬蟲記憶體洩露
- 面試:為了進阿里,死磕了ThreadLocal記憶體洩露原因面試阿里thread記憶體洩露
- win10驅動記憶體洩露如何解決_win10記憶體洩露處理方法Win10記憶體洩露
- Spring Boot heapdump洩露記憶體分析方法Spring Boot記憶體
- 線上記憶體洩露定位--memleak工具記憶體洩露
- java中如何檢視記憶體洩露Java記憶體洩露
- android Handler導致的記憶體洩露Android記憶體洩露
- netty 堆外記憶體洩露排查盛宴Netty記憶體洩露
- 乾貨分享:淺談記憶體洩露記憶體洩露
- 解決git記憶體洩露問題Git記憶體洩露
- 記一次"記憶體洩露"排查過程記憶體洩露
- Python實現記憶體洩露排查的示例Python記憶體洩露
- C程式記憶體洩露檢測工具——ValgrindC程式記憶體洩露
- Android效能最佳化之記憶體洩露Android記憶體洩露
- 小題大做 | Handler記憶體洩露全面分析記憶體洩露
- 簡單的記憶體“洩露”和“溢位”記憶體
- JAVA記憶體洩露的原因及解決Java記憶體洩露
- 一個 Vue 頁面的記憶體洩露分析Vue記憶體洩露
- 一個Vue頁面的記憶體洩露分析Vue記憶體洩露
- 記一次 .NET 某工控軟體 記憶體洩露分析記憶體洩露
- 使用mtrace追蹤JVM堆外記憶體洩露JVM記憶體洩露
- 一次Kafka記憶體洩露排查經過Kafka記憶體洩露
- 利用dotnet-dump分析docker容器記憶體洩露Docker記憶體洩露
- 前端面試題51----JS記憶體洩露前端面試題JS記憶體洩露
- ThreadLocal原始碼解讀和記憶體洩露分析thread原始碼記憶體洩露
- @Transactional千萬不要這樣用!!踩坑了你都可能發現不了!!!
- 從記憶體洩露、記憶體溢位和堆外記憶體,JVM優化引數配置引數記憶體洩露記憶體溢位JVM優化
- 案例:隱秘而低調的記憶體洩露(OOM)記憶體洩露OOM
- 使用Windbg快速分析應用記憶體洩露問題記憶體洩露