最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?中的5個(gè)代碼性能提升技巧

 更新時(shí)間:2021年12月24日 08:47:36   作者:程序猿阿朗  
這篇文章主要給大家分享了Java的5個(gè)代碼性能提升的技巧,雖然大多數(shù)情況下極致優(yōu)化代碼是沒(méi)有必要的,但是作為一名技術(shù)開(kāi)發(fā)者,我們還是想追求代碼的更小、更快,更強(qiáng)。如果哪天發(fā)現(xiàn)程序的運(yùn)行速度不盡人意,就需要這樣的文章了,需要的朋友可以參考一下

前言:

提示:我們不應(yīng)該為了優(yōu)化而優(yōu)化,這有時(shí)會(huì)增加代碼的復(fù)雜度。

這篇文章中的代碼都在以下環(huán)境中進(jìn)行性能測(cè)試。

  • JMH version: 1.33(Java 基準(zhǔn)測(cè)試框架)
  • VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724

通過(guò)這篇文章的測(cè)試,將發(fā)現(xiàn)以下幾個(gè)操作的性能差異。

  • 預(yù)先分配 HashMap 的大小,提高 1/4 的性能。
  • 優(yōu)化 HashMap key,性能相差 9.5 倍。
  • 不使用 Enum.values() 遍歷,Spring 也曾如此優(yōu)化。
  • 使用 Enum 代替 String 常量,性能高出 1.5 倍。
  • 使用高版本 JDK,基礎(chǔ)操作有 2-5 倍性能差異。

1.預(yù)先分配 HashMap 的大小

HashMap 是 Java 中最為常用的集合之一,大多數(shù)的操作速度都非???,但是 HashMap 在調(diào)整自身的容量大小時(shí)是很慢且難以自動(dòng)優(yōu)化,因此我們?cè)诙x一個(gè) HashMap 之前,應(yīng)該盡可能的給出它的容量大小。給出 size 值時(shí)要考慮負(fù)載因子,HashMap 默認(rèn)負(fù)載因子是 0.75,也就是要設(shè)置的 size 值要除于 0.75。

相關(guān)文章:HashMap 源碼分析解讀

下面使用 JMH 進(jìn)行基準(zhǔn)測(cè)試,測(cè)試分別向初始容量為 16 和 32 的 HashMap 中插入 14 個(gè)元素的效率。

/**
 * @author https://www.wdbyte.com
 */
@State(Scope.Benchmark)
@Warmup(iterations = 3,time = 3)
@Measurement(iterations = 5,time = 3)
public class HashMapSize {

    @Param({"14"})
    int keys;

    @Param({"16", "32"})
    int size;

    @Benchmark
    public HashMap<Integer, Integer> getHashMap() {
        HashMap<Integer, Integer> map = new HashMap<>(size);
        for (int i = 0; i < keys; i++) {
            map.put(i, i);
        }
        return map;
    }
}

HashMap 的初始容量是 16,負(fù)責(zé)因子 0.75,即最多插入 12 個(gè)元素,再插入時(shí)就要進(jìn)行擴(kuò)容,所以插入 14 個(gè)元素過(guò)程中需要擴(kuò)容一次,但是如果 HashMap 初始化時(shí)就給了 32 容量,那么最多可以承載 32 * 0.75 = 24 個(gè)元素,所以插入 14 個(gè)元素時(shí)是不需要擴(kuò)容操作的。

# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724

Benchmark               (keys)  (size)   Mode  Cnt        Score        Error  Units
HashMapSize.getHashMap      14      16  thrpt   25  4825825.152 ± 323910.557  ops/s
HashMapSize.getHashMap      14      32  thrpt   25  6556184.664 ± 711657.679  ops/s


可以看到在這次測(cè)試中,初始容量為32 的 HashMap 比初始容量為 16 的 HashMap 每秒可以多操作 26% 次,已經(jīng)有 1/4 的性能差異了。

2.優(yōu)化 HashMap 的 key

如果 HashMap 的 key 值需要用到多個(gè) String 字符串時(shí),把字符串作為某個(gè)類屬性,然后使用這個(gè)類的實(shí)例作為 key 會(huì)比使用字符串拼接效率更高。

下面測(cè)試使用兩個(gè)字符串拼接作為 key,和把兩個(gè)字符串作為 MutablePair 類的屬性引用,然后使用 MutablePair 對(duì)象作為 key 的運(yùn)行效率差異。

/**
 * @author https://www.wdbyte.com
 */
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class HashMapKey {

    private int size = 1024;
    private Map<String, Object> stringMap;
    private Map<Pair, Object> pairMap;
    private String[] prefixes;
    private String[] suffixes;

    @Setup(Level.Trial)
    public void setup() {
        prefixes = new String[size];
        suffixes = new String[size];
        stringMap = new HashMap<>();
        pairMap = new HashMap<>();
        for (int i = 0; i < size; ++i) {
            prefixes[i] = UUID.randomUUID().toString();
            suffixes[i] = UUID.randomUUID().toString();
            stringMap.put(prefixes[i] + ";" + suffixes[i], i);
            // use new String to avoid reference equality speeding up the equals calls
            pairMap.put(new MutablePair(prefixes[i], suffixes[i]), i);
        }
    }

    @Benchmark
    @OperationsPerInvocation(1024)
    public void stringKey(Blackhole bh) {
        for (int i = 0; i < prefixes.length; i++) {
            bh.consume(stringMap.get(prefixes[i] + ";" + suffixes[i]));
        }
    }

    @Benchmark
    @OperationsPerInvocation(1024)
    public void pairMap(Blackhole bh) {
        for (int i = 0; i < prefixes.length; i++) {
            bh.consume(pairMap.get(new MutablePair(prefixes[i], suffixes[i])));
        }
    }
}

測(cè)試結(jié)果:

# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724

Benchmark              Mode  Cnt         Score         Error  Units
HashMapKey.pairMap    thrpt   25  89295035.436 ± 6498403.173  ops/s
HashMapKey.stringKey  thrpt   25   9410641.728 ±  389850.653  ops/s

可以發(fā)現(xiàn)使用對(duì)象引用作為 key 的性能,是使用 String 拼接作為 key 的性能的 9.5 倍。

3.不使用 Enum.values() 遍歷

我們通常會(huì)使用 Enum.values() 進(jìn)行枚舉類遍歷,但是這樣每次調(diào)用都會(huì)分配枚舉類值數(shù)量大小的數(shù)組用于操作,這里完全可以緩存起來(lái),以減少每次內(nèi)存分配的時(shí)間和空間消耗。

/**
 * 枚舉類遍歷測(cè)試
 *
 * @author https://www.wdbyte.com
 */
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class EnumIteration {
    enum FourteenEnum {
        a,b,c,d,e,f,g,h,i,j,k,l,m,n;

        static final FourteenEnum[] VALUES;
        static {
            VALUES = values();
        }
    }

    @Benchmark
    public void valuesEnum(Blackhole bh) {
        for (FourteenEnum value : FourteenEnum.values()) {
            bh.consume(value.ordinal());
        }
    }

    @Benchmark
    public void enumSetEnum(Blackhole bh) {
        for (FourteenEnum value : EnumSet.allOf(FourteenEnum.class)) {
            bh.consume(value.ordinal());
        }
    }

    @Benchmark
    public void cacheEnums(Blackhole bh) {
        for (FourteenEnum value : FourteenEnum.VALUES) {
            bh.consume(value.ordinal());
        }
    }
}

運(yùn)行結(jié)果:

# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724

Benchmark                   Mode  Cnt         Score         Error  Units
EnumIteration.cacheEnums   thrpt   25  15623401.567 ± 2274962.772  ops/s
EnumIteration.enumSetEnum  thrpt   25   8597188.662 ±  610632.249  ops/s
EnumIteration.valuesEnum   thrpt   25  14713941.570 ±  728955.826  ops/s

很明顯使用緩存后的遍歷速度是最快的,使用 EnumSet 遍歷效率是最低的,這很好理解,數(shù)組的遍歷效率是大于哈希表的。

可能你會(huì)覺(jué)得這里使用 values() 緩存和直接使用 Enum.values() 的效率差異很小,其實(shí)在某些調(diào)用頻率很高的場(chǎng)景下是有很大區(qū)別的,在 Spring 框架中,曾使用 Enum.values() 這種方式在每次響應(yīng)時(shí)遍歷 HTTP 狀態(tài)碼枚舉類,這在請(qǐng)求量大時(shí)造成了不必要的性能開(kāi)銷,后來(lái)進(jìn)行了 values() 緩存優(yōu)化。

下面是這次提交的截圖:

4.使用 Enum 代替 String 常量

使用 Enum 枚舉類代替 String 常量有明顯的好處,枚舉類強(qiáng)制驗(yàn)證,不會(huì)出錯(cuò),同時(shí)使用枚舉類的效率也更高。即使作為 Map 的 key 值來(lái)看,雖然 HashMap 的速度已經(jīng)很快了,但是使用 EnumMap 的速度可以更快。

提示:不要為了優(yōu)化而優(yōu)化,這會(huì)增加代碼的復(fù)雜度。

下面測(cè)試使用使用 Enum 作為 key,和使用 String 作為 key,在 map.get 操作下的性能差異。

/**
 * @author https://www.wdbyte.com
 */
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class EnumMapBenchmark {

    enum AnEnum {
        a, b, c, d, e, f, g,
        h, i, j, k, l, m, n,
        o, p, q,    r, s, t,
        u, v, w,    x, y, z;
    }

    /** 要查找的 key 的數(shù)量 */
    private static int size = 10000;
    /** 隨機(jī)數(shù)種子 */
    private static int seed = 99;

    @State(Scope.Benchmark)
    public static class EnumMapState {
        private EnumMap<AnEnum, String> map;
        private AnEnum[] values;

        @Setup(Level.Trial)
        public void setup() {
            map = new EnumMap<>(AnEnum.class);
            values = new AnEnum[size];
            AnEnum[] enumValues = AnEnum.values();
            SplittableRandom random = new SplittableRandom(seed);
            for (int i = 0; i < size; i++) {
                int nextInt = random.nextInt(0, Integer.MAX_VALUE);
                values[i] = enumValues[nextInt % enumValues.length];
            }
            for (AnEnum value : enumValues) {
                map.put(value, UUID.randomUUID().toString());
            }
        }
    }

    @State(Scope.Benchmark)
    public static class HashMapState{
        private HashMap<String, String> map;
        private String[] values;

        @Setup(Level.Trial)
        public void setup() {
            map = new HashMap<>();
            values = new String[size];
            AnEnum[] enumValues = AnEnum.values();
            int pos = 0;
            SplittableRandom random = new SplittableRandom(seed);
            for (int i = 0; i < size; i++) {
                int nextInt = random.nextInt(0, Integer.MAX_VALUE);
                values[i] = enumValues[nextInt % enumValues.length].toString();
            }
            for (AnEnum value : enumValues) {
                map.put(value.toString(), UUID.randomUUID().toString());
            }
        }
    }

    @Benchmark
    public void enumMap(EnumMapState state, Blackhole bh) {
        for (AnEnum value : state.values) {
            bh.consume(state.map.get(value));
        }
    }

    @Benchmark
    public void hashMap(HashMapState state, Blackhole bh) {
        for (String value : state.values) {
            bh.consume(state.map.get(value));
        }
    }
}

運(yùn)行結(jié)果:

# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724

Benchmark????????????????? Mode? Cnt????? Score????? Error? Units
EnumMapBenchmark.enumMap? thrpt?? 25? 22159.232 ± 1268.800? ops/s
EnumMapBenchmark.hashMap? thrpt?? 25? 14528.555 ± 1323.610? ops/s

很明顯,使用 Enum 作為 key 的性能比使用 String 作為 key 的性能高出 1.5 倍。但是仍然要根據(jù)實(shí)際情況考慮是否使用 EnumMap EnumSet。

5.使用高版本 JDK

String 類應(yīng)該是 Java 中使用頻率最高的類了,但是 Java 8 中的 String 實(shí)現(xiàn)相比高版本 JDK ,則占用空間更多,性能更低。

下面測(cè)試 String 轉(zhuǎn) bytes 和 bytes 轉(zhuǎn) String 在 Java 8 以及 Java 11 中的性能開(kāi)銷。

/**
 * @author https://www.wdbyte.com
 * @date 2021/12/23
 */
@State(Scope.Benchmark)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class StringInJdk {

    @Param({"10000"})
    private int size;
    private String[] stringArray;
    private List<byte[]> byteList;

    @Setup(Level.Trial)
    public void setup() {
        byteList = new ArrayList<>(size);
        stringArray = new String[size];
        for (int i = 0; i < size; i++) {
            String uuid = UUID.randomUUID().toString();
            stringArray[i] = uuid;
            byteList.add(uuid.getBytes(StandardCharsets.UTF_8));
        }
    }

    @Benchmark
    public void byteToString(Blackhole bh) {
        for (byte[] bytes : byteList) {
            bh.consume(new String(bytes, StandardCharsets.UTF_8));
        }
    }

    @Benchmark
    public void stringToByte(Blackhole bh) {
        for (String s : stringArray) {
            bh.consume(s.getBytes(StandardCharsets.UTF_8));
        }
    }
}

測(cè)試結(jié)果:

# JMH version: 1.33
# VM version: JDK 1.8.0_151, Java HotSpot(TM) 64-Bit Server VM, 25.151-b12

Benchmark???????????????? (size)?? Mode? Cnt???? Score???? Error? Units
StringInJdk.byteToString?? 10000? thrpt?? 25? 2396.713 ± 133.500? ops/s
StringInJdk.stringToByte?? 10000? thrpt?? 25? 1745.060 ±? 16.945? ops/s

# JMH version: 1.33
# VM version: JDK 17, OpenJDK 64-Bit Server VM, 17+35-2724

Benchmark???????????????? (size)?? Mode? Cnt???? Score???? Error? Units
StringInJdk.byteToString?? 10000? thrpt?? 25? 5711.954 ±? 41.865? ops/s
StringInJdk.stringToByte?? 10000? thrpt?? 25? 8595.895 ± 704.004? ops/s

可以看到在 bytes 轉(zhuǎn) String 操作上,Java 17 的性能是 Java 8 的 2.5 倍左右,而 String 轉(zhuǎn) bytes 操作,Java 17 的性能是 Java 8 的 5 倍。關(guān)于字符串的操作非?;A(chǔ),隨處可見(jiàn),可見(jiàn)高版本的優(yōu)勢(shì)十分明顯。

到此這篇關(guān)于Java 中的5個(gè)代碼性能提升技巧的文章就介紹到這了,更多相關(guān)Java 代碼性能提升技巧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

參考:

https://richardstartin.github.io/posts/5-java-mundane-performance-tricks
https://github.com/spring-projects/spring-framework/issues/26842
https://github.com/spring-projects/spring-framework/commit/7f1062159ee9926d5abed7cadc2b36b6b7fc242e

相關(guān)文章

  • JAVA獲取特定格式時(shí)間方式

    JAVA獲取特定格式時(shí)間方式

    我們有時(shí)要獲取時(shí)間,年月日時(shí)分秒周幾,有時(shí)要以特定的格式出現(xiàn),本文主要介紹了JAVA獲取特定格式時(shí)間方式,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • Mybatis如何一次性插入多條數(shù)據(jù)

    Mybatis如何一次性插入多條數(shù)據(jù)

    這篇文章主要介紹了Mybatis如何一次性插入多條數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 使用javassist動(dòng)態(tài)生成類的配置代碼

    使用javassist動(dòng)態(tài)生成類的配置代碼

    Javassist它是一個(gè)用 Java 編輯字節(jié)碼的類庫(kù),它使 Java 程序能夠在運(yùn)行時(shí)定義新類,并在 JVM 加載時(shí)修改類文件,本文給大家介紹使用javassist動(dòng)態(tài)生成類的實(shí)例代碼,感興趣的朋友一起看看吧
    2022-09-09
  • SpringBoot?的?web?類型推斷詳解

    SpringBoot?的?web?類型推斷詳解

    這篇文章主要介紹了SpringBoot?的?web?類型推斷詳解的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Spring三級(jí)緩存解決循環(huán)依賴的過(guò)程分析

    Spring三級(jí)緩存解決循環(huán)依賴的過(guò)程分析

    這篇文章主要介紹了Spring三級(jí)緩存解決循環(huán)依賴,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • Java并發(fā)編程中的ReentrantLock類詳解

    Java并發(fā)編程中的ReentrantLock類詳解

    這篇文章主要介紹了Java并發(fā)編程中的ReentrantLock類詳解,ReentrantLock是juc.locks包中的一個(gè)獨(dú)占式可重入鎖,相比synchronized,它可以創(chuàng)建多個(gè)條件等待隊(duì)列,還支持公平/非公平鎖、可中斷、超時(shí)、輪詢等特性,需要的朋友可以參考下
    2023-12-12
  • Java Mybatis批量修改封裝詳解

    Java Mybatis批量修改封裝詳解

    這篇文章主要介紹了Mybatis批量修改封裝的相關(guān)內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java基于IDEA實(shí)現(xiàn)qq郵件發(fā)送小程序

    Java基于IDEA實(shí)現(xiàn)qq郵件發(fā)送小程序

    這篇文章主要介紹了Java基于IDEA實(shí)現(xiàn)qq郵件發(fā)送小程序功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • 在idea中全局引入并運(yùn)行ElementUI方式

    在idea中全局引入并運(yùn)行ElementUI方式

    本文詳細(xì)描述了如何在IDEA中使用ElementUI,包括從官網(wǎng)獲取連接、在IDEA終端運(yùn)行命令安裝ElementUI,以及如何在項(xiàng)目中全局引入ElementUI,通過(guò)新建頁(yè)面并配置index.js和ElementUI.vue,可以實(shí)現(xiàn)在本地服務(wù)器上的展示
    2024-10-10
  • IDEA 錯(cuò)誤之找不到或無(wú)法加載主類的問(wèn)題

    IDEA 錯(cuò)誤之找不到或無(wú)法加載主類的問(wèn)題

    這篇文章主要介紹了IDEA 錯(cuò)誤之找不到或無(wú)法加載主類,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08

最新評(píng)論

湄潭县| 长宁县| 永春县| 济源市| 兰西县| 章丘市| 台州市| 义马市| 哈巴河县| 漠河县| 任丘市| 吕梁市| 南和县| 五台县| 当涂县| 河西区| 天等县| 婺源县| 永和县| 镇康县| 贺兰县| 格尔木市| 高唐县| 张家川| 重庆市| 洞头县| 南充市| 环江| 进贤县| 临沧市| 游戏| 监利县| 西乌| 长岛县| 武功县| 桦甸市| 明光市| 黄冈市| 南宁市| 台州市| 肥西县|