乾物!JAvaコード性能最適化のいくつかの提案


骐骥は一躍、十歩もできない.駑馬十駆、功は惜しくない.コードの品質を高め、性能を最適化するには、堅持することが重要です.コードの「悪い味」を消して積み重ねていけば、自分のコードレベルを高めるだけでなく、コードを「
精白無一徹」.
私达はバックエンドの开発の中で、いつも各种の“穴”に出会うことができて、例えばみんなの最も熟知している空の指针の异常、sessionの失効など、実は大きい原因は开発の过程の中でとても良い使い方と実际を结び付けていないので、もし私达がいくつかのコードの规范と技巧に注意することができれば、私达の开発の事を半分の功倍にすることができます!開発中に行った最適化を以下の3つの面から共有します.
  • コードのパフォーマンスを向上させる
  • コードをより優雅にする
  • バグ
  • からコードを遠ざける
    コードのパフォーマンスを向上
    1..Mapのプライマリ・キーと値が必要な場合はentrySet()を反復する必要があります.
    反復keySet()は、ループ中にMapのプライマリキーのみが必要な場合に正しい.ただし、プライマリ・キーと値を取得する必要がある場合は、keySet()を反復してからgetを取得するよりも効率的です.
    反例
    Map map = ...;
    for(String key: map.keySet()) {
        String value = map.get(key);
        ...
    }
    
    

    正例
    Map map = ...;
    for (Map.Entry entry : map.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        ...
    }

    2.Collection.isEmpty()を使用して空を検出する必要があります
    Collection.size()を使用して空の論理を検出しても問題はありませんが、Collection.isEmpty()を使用するとコードが読みやすくなり、パフォーマンスが向上します.任意のCollection.isEmpty()実装の時間的複雑さはO(1)であるが、いくつかのCollection.size()実装の時間的複雑さはO(n)である可能性がある.
    反例
    if (collection.size() == 0) {
        ...
    }

    正例
    if (collection.isEmpty()) {
        ...
    }

    3.集合初期化できるだけサイズを指定する
    JAvaの集合クラスは使いやすいが,ソースコードを見ると集合にも大きさの制限がある.毎回の拡容の時間的複雑さはO(n)である可能性が高いので,できるだけ予知可能な集合の大きさを指定し,集合の拡容回数を減らすことができる.
    反例
    int[] arr = new int[]{1, 2, 3};
    List list = new ArrayList<>();
    for (int i : arr) {
        list.add(i);
    }

    正例
    int[] arr = new int[]{1, 2, 3};
    List list = new ArrayList<>(arr.length);
    for (int i : arr) {
        list.add(i);
    }

    4.StringBuilderを使用した文字列接合
    一般的な文字列パッチはコンパイル期間javaで最適化されますが、ループで文字列パッチはjavaコンパイル期間では最適化できないため、StringBuilderで置き換える必要があります.反例
    String s = "";
    for (int i = 0; i < 10; i++) {
        s += i;
    }

    正例
    String a = "a";
    String b = "b";
    String c = "c";
    String s = a + b + c; //    ,java        
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10; i++) {
        sb.append(i);  //    ,java         ,       StringBuilder
    }

    5.Listのランダムアクセス
    配列とチェーンテーブルの違い:配列のランダムアクセス効率が向上します.呼び出しメソッドがListを取得した後、その中のデータにランダムにアクセスしたい場合、その配列内部実装がチェーンテーブルなのか配列なのか分からない場合は、どうすればいいのでしょうか.RandomAccessインタフェースが実装されているかどうかを判断できます.
    正例
    //        list
    List list = otherService.getList();
    if (list instanceof RandomAccess) {
        //       ,      
        System.out.println(list.get(list.size() - 1));
    } else {
        //          ,       
    }

    6.Collection.containsメソッドを頻繁に呼び出すにはSetを使用します.
    Javaコレクションクラスライブラリでは,Listのcontainsメソッドの一般的な時間複雑度はO(n)であり,コードでcontainsメソッドを頻繁に呼び出してデータを検索する必要がある場合,listをHashSetに変換して実装し,O(n)の時間複雑度をO(1)に下げることができる.
    反例
    ArrayList list = otherService.getList();
    for (int i = 0; i <= Integer.MAX_VALUE; i++) {
        //      O(n)
        list.contains(i);
    }

    正例
    ArrayList list = otherService.getList();
    Set set = new HashSet(list);
    for (int i = 0; i <= Integer.MAX_VALUE; i++) {
        //      O(1)
        set.contains(i);
    }

    コードを優雅にする
    1.ロング定数後に大文字Lを追加
    反例
    long value = 1l;
    long max = Math.max(1L, 5);

    正例
    long value = 1L;
    long max = Math.max(1L, 5L);

    2.魔法値は使用しない
    コードを作成すると、魔法の値を使用すると明確に見えるかもしれませんが、デバッグ時にはそれほど明確ではありません.これが魔法値を読み取り可能定数として定義する必要がある理由です.ただし、-1、0、1は魔法値とみなされません.
    反例
    for (int i = 0; i < 100; i++){
        ...
    }
    if (a == 100) {
        ...
    }

    正例
    private static final int MAX_COUNT = 100;
    for (int i = 0; i < MAX_COUNT; i++){
        ...
    }
    if (count == MAX_COUNT) {
        ...
    }

    3.集合インプリメンテーションを使用して静的メンバー変数を付与しない
    集合タイプの静的メンバー変数では、集合インプリメンテーションを使用して値を割り当てるのではなく、静的コードブロックを使用して値を割り当てる必要があります.
    反例
    private static Map map = new HashMap() {
        {
            put("a", 1);
            put("b", 2);
        }
    };
    
    
    private static List list = new ArrayList() {
        {
            add("a");
            add("b");
        }
    };

    正例
    private static Map map = new HashMap<>();
    static {
        map.put("a", 1);
        map.put("b", 2);
    };
    
    private static List list = new ArrayList<>();
    static {
        list.add("a");
        list.add("b");
    };

    4.未使用のプライベートメソッドとフィールドの削除
    未使用のプライベートメソッドとフィールドを削除し、コードをより簡潔に維持しやすくします.再利用が必要な場合は、履歴提出から取り戻すことができます.
    反例
    public class DoubleDemo1 {
        private int unusedField = 100;
        private void unusedMethod() {
            ...
        }
        public int sum(int a, int b) {
            return a + b;
        }
    }

    正例
    public class DoubleDemo1 {
        public int sum(int a, int b) {
            return a + b;
        }
    }

    5.ツールクラスは構造関数を遮断すべきである
    ツールクラスは静的フィールドと関数のセットであり、インスタンス化すべきではありません.ただし、Javaは、コンストラクション関数を明確に定義していないクラスごとに暗黙的な公有コンストラクション関数を追加します.したがって、javaの「白」の使用が間違っていることを避けるために、この暗黙的な公有構造関数を遮断するために、プライベート構造関数を明示的に定義する必要があります.反例
    public class MathUtils {
        public static final double PI = 3.1415926D;
        public static int sum(int a, int b) {
            return a + b;
        }
    }

    正例
    public class MathUtils {
        public static final double PI = 3.1415926D;
        private MathUtils() {}
        public static int sum(int a, int b) {
            return a + b;
        }
    }

    6.公有静定数はクラスを通じてアクセスすべきである
    クラスのインスタンスを介して公有静的定数にアクセスすることは可能であるが、各クラスのインスタンスに公有静的定数があると勘違いされやすい.したがって、公有静的定数はクラスを介して直接アクセスする必要があります.
    反例
    public class User {
        public static final String CONST_NAME = "name";
        ...
    }
    User user = new User();
    String nameKey = user.CONST_NAME;

    正例
    public class User {
        public static final String CONST_NAME = "name";
        ...
    }
    String nameKey = User.CONST_NAME;

    7.Null PointerExceptionで空を判断しない
    空のポインタ異常は、異常をキャプチャする方法で処理するのではなく、コードで回避する必要があります(たとえば、検出が空ではありません).
    反例
    public String getUserName(User user) {
        try {
            return user.getName();
        } catch (NullPointerException e) {
            return null;
        }
    }

    正例
    public String getUserName(User user) {
        if (Objects.isNull(user)) {
            return null;
        }
        return user.getName();
    }
    

    8."+valueの代わりにString.valueOf(value)を使用
    他のオブジェクトまたはタイプを文字列に変換する場合は、String.valueOf(value)を使用すると、"+valueよりも効率が高くなります.
    反例
    int i = 1;
    String s = "" + i;
    

    正例
    int i = 1;
    String s = String.valueOf(i);

    9.古いコードに@Deprecated注記を追加
    一部のコードが古いが、互換性のために直接削除することができず、後で誰かが使用したくない場合は、@Deprescated注釈を追加してタグを付けることができます.ドキュメントコメントに@deprecatedを追加して説明し、代替案を提供します.
    正例
    /**
     *   
     *
     * @deprecated        ,   {@link newSave()}     
     */
    @Deprecated
    public void save(){
        // do something
    }

    コードをバグから遠ざける
    1.構造方法の使用禁止BigDecimal(double)
    BigDecimal(double)は精度損失のリスクがあり、正確な計算や値比較のシーンでビジネスロジックが異常になる可能性があります.
    反例
    BigDecimal value = new BigDecimal(0.1D); // 0.100000000000000005551115...

    正例
    BigDecimal value = BigDecimal.valueOf(0.1D);; // 0.1

    2.nullではなく空の配列と空の集合を返します.
    nullを返すには、呼び出し元がnullを強制的に検出する必要があります.そうしないと、空のポインタ異常が放出されます.空の配列または空の集合を返します.nullが検出されていないため、呼び出し元が空のポインタを放出する異常を効果的に回避します.また、呼び出し元がnullを検出する文を削除してコードをより簡潔にすることもできます.
    反例
    
    public static Result[] getResults() {
        return null;
    }
    
    
    public static List getResultList() {
        return null;
    }
    
    
    public static Map getResultMap() {
        return null;
    }
    
    
    public static void main(String[] args) {
        Result[] results = getResults();
        if (results != null) {
            for (Result result : results) {
                ...
            }
        }
    
    
        List resultList = getResultList();
        if (resultList != null) {
            for (Result result : resultList) {
                ...
            }
        }
    
    
        Map resultMap = getResultMap();
        if (resultMap != null) {
            for (Map.Entry resultEntry : resultMap) {
                ...
            }
        }
    }

    正例
    public static Result[] getResults() {
        return new Result[0];
    }
    
    
    public static List getResultList() {
        return Collections.emptyList();
    }
    
    
    public static Map getResultMap() {
        return Collections.emptyMap();
    }
    
    
    public static void main(String[] args) {
        Result[] results = getResults();
        for (Result result : results) {
            ...
        }
    
    
        List resultList = getResultList();
        for (Result result : resultList) {
            ...
        }
    
    
        Map resultMap = getResultMap();
        for (Map.Entry resultEntry : resultMap) {
            ...
        }
    }
    

    3.定数または決定値を優先的に使用してequalsメソッドを呼び出す
    オブジェクトのequalsメソッドはポインタ異常を空にしやすく、定数または値のあるオブジェクトを決定してequalsメソッドを呼び出す必要があります.もちろんjava.util.Objects.equals()メソッドを使用するのがベストです.
    反例
    public void isFinished(OrderStatus status) {
        return status.equals(OrderStatus.FINISHED); //         
    }

    正例
    public void isFinished(OrderStatus status) {
        return OrderStatus.FINISHED.equals(status);
    }
    
    
    public void isFinished(OrderStatus status) {
        return Objects.equals(status, OrderStatus.FINISHED);
    }

    4.列挙された属性フィールドは、非公開である必要があります.
    列挙は通常定数として使用され、列挙に共通属性フィールドが存在する場合やフィールドメソッドを設定する場合、これらの列挙定数の属性は変更されやすい.理想的には、列挙中の属性フィールドはプライベートであり、プライベート構造関数に値を付与し、対応するSetterメソッドがなく、final修飾子を付けることが望ましい.
    反例
    public enum UserStatus {
        DISABLED(0, "  "),
        ENABLED(1, "  ");
    
    
        public int value;
        private String description;
    
    
        private UserStatus(int value, String description) {
            this.value = value;
            this.description = description;
        }
    
    
        public String getDescription() {
            return description;
        }
    
    
        public void setDescription(String description) {
            this.description = description;
        }
    }

    正例
    public enum UserStatus {
        DISABLED(0, "  "),
        ENABLED(1, "  ");
    
    
        private final int value;
        private final String description;
    
    
        private UserStatus(int value, String description) {
            this.value = value;
            this.description = description;
        }
    
    
        public int getValue() {
            return value;
        }
    
    
        public String getDescription() {
            return description;
        }
    }

    5.String.split(String regex)に注意
    文字列Stringのsplitメソッドで、入力された区切り文字列は正規表現です!一部のキーワード(.[]()|など)は、エスケープが必要です.
    反例
    "a.ab.abc".split("."); //    []
    "a|ab|abc".split("|"); //    ["a", "|", "a", "b", "|", "a", "b", "c"]

    正例
    "a.ab.abc".split("\\."); //    ["a", "ab", "abc"]
    "a|ab|abc".split("\\|"); //    ["a", "ab", "abc"]

    以上の部分は雲栖コミュニティを参考にしてください!