Androidでの列挙型の使用は厳密に避けるべきですか?


92

Bundle以下のようなインターフェースで、キーのような関連する定数のセットを一緒に定義するために使用しました:

public interface From{
    String LOGIN_SCREEN = "LoginSCreen";
    String NOTIFICATION = "Notification";
    String WIDGET = "widget";
}

これにより、関連する定数をグループ化し、静的なインポート(実装ではない)を使用してそれらを使用するより良い方法が得られます。私が知っているAndroidフレームワークはまた、のような同じように定数を使用していますToast.LENTH_LONGView.GONE

しかし、私はしばしばJava Enums、定数を表すためのはるかに優れた強力な方法を提供していると感じています。

しかし、での使用enumsにパフォーマンスの問題はありAndroidますか?

少し調べただけで混乱してしまいました。この質問から、 「Aint Enums Where You Only Ints Needs」がAndroidのパフォーマンスのヒントから削除されましたか?「Avoid enums」がパフォーマンスのヒントからGoogle削除されたことは明らかですが、公式のトレーニングドキュメントからメモリオーバーヘッドセクションに注意してください「列挙型多くの場合、静的定数の2倍以上のメモリが必要です。Androidでの列挙型の使用は厳密に避けてください。」これはまだ有効ですか?(1.6以降のバージョンなど)Java

私が観察したもう1つの問題は、シリアル化して送信する必要があることenumsintents使用しBundleて送信することです(つまりputSerializable()、プリミティブputString()メソッドに比べて高価な操作だと思いますが、enums無料で提供しています)。

誰かが同じものを表現するのに最適な方法を明確にできますかAndroid?私は厳密には使用しないでくださいenumsAndroid


5
入手可能なツールを使用する必要があります。実際、アクティビティまたはフラグメントは多くのメモリとCPU使用率を消費しますが、それがそれらの使用を停止する理由にはなりません。必要な場合は静的整数を使用し、必要な場合は列挙型を使用します。
Patrick

11
同意する。これは時期尚早の最適化のようなにおいがします。パフォーマンスやメモリの問題が発生しておらず、列挙型が原因であることがプロファイリングで証明できる場合を除き、意味のある場所で列挙型を使用してください。
GreyBeardedGeek 2015年

2
以前は、列挙型が重要でないパフォーマンスペナルティを被ったと信じられていましたが、最近のベンチマークでは、代わりに定数を使用するメリットは示されていません。参照してくださいstackoverflow.com/questions/24491160/...などstackoverflow.com/questions/5143256/...
ベンジャミンセルジャン

1
バンドル内のEnumをシリアル化することによるパフォーマンスの低下を回避するには、Enum.ordinal()代わりに使用してintとして渡すことができます。
BladeCoder 2015

1
最後にEnumのパフォーマンスの問題に関するいくつかの説明があります youtube.com/watch?v=Hzs6OBcvNQE
nvinayshetty

回答:


115

enumその機能が必要なときに使用します。それを避けるしないでください厳密に

Java enumはより強力ですが、その機能が必要ない場合は定数を使用すると、占有するスペースが少なくなり、プリミティブにすることができます。

列挙型を使用する場合:

  • タイプチェック- リストされた値のみを受け入れることができ、それらは連続的ではありません(以下でここで連続と呼ぶものを参照)
  • メソッドのオーバーロード-すべてのenum定数にはメソッドの独自の実装があります

    public enum UnitConverter{
        METERS{
            @Override
            public double toMiles(final double meters){
                return meters * 0.00062137D;
            }
    
            @Override
            public double toMeters(final double meters){
                return meters;
            }
        },
        MILES{
            @Override
            public double toMiles(final double miles){
                return miles;
            }
    
            @Override
            public double toMeters(final double miles){
                return miles / 0.00062137D;
            }
        };
    
        public abstract double toMiles(double unit);
        public abstract double toMeters(double unit);
    }
    
  • より多くのデータ-1つの定数に、1つの変数に入れることができない複数の情報が含まれています

  • 複雑なデータ-データを操作するために常に必要なメソッド

列挙型を使用しない場合:

  • 1つのタイプのすべての値を受け入れることができ、定数には最もよく使用される値のみが含まれます
  • 継続的なデータを受け入れることができます

    public class Month{
        public static final int JANUARY = 1;
        public static final int FEBRUARY = 2;
        public static final int MARCH = 3;
        ...
    
        public static String getName(final int month){
            if(month <= 0 || month > 12){
                throw new IllegalArgumentException("Invalid month number: " + month);
            }
    
            ...
        }
    }
    
  • 名前(例のように)
  • 列挙型を本当に必要としない他のすべてのために

列挙型はより多くのスペースを占有します

  • enum定数への単一の参照は4バイトを占有します
  • すべてのenum定数は、8バイトに整列されたフィールドのサイズの合計 + オブジェクトのオーバーヘッドであるスペースを占有します
  • enumクラス自体がいくつかのスペースを占有します

定数が占めるスペースが少ない

  • 定数には参照がないため、それは純粋なデータです(参照であっても、列挙インスタンスは別の参照への参照になります)
  • 定数は既存のクラスに追加できます-別のクラスを追加する必要はありません
  • 定数はインライン化できます。コンパイル時の拡張機能(nullチェック、デッドコードの検出など)を提供します。

2
@IntDefアノテーションを使用して、int定数の型チェックをシミュレートできます。Java Enumsを支持する議論は1つ少なくなります。
BladeCoder 2015

3
ノー@BladeCoder、あなたは定数への参照を必要としない
カミルJarosz

1
また、列挙型を使用するとリフレクションの使用が促進されることに注意してください。また、リフレクションの使用はAndroidのパフォーマンスに大きな影響を与えることが知られています。こちらをご覧ください:blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark

3
@ w3bshark列挙型はリフレクションの使用をどのように促進しますか?
Kevin Krumwiede 2017年

3
もう1つ覚えておかなければならないのは、標準の空の "Hello、world!"からのヒープダンプです。プロジェクトには、4,000を超えるクラスと700,000のオブジェクトが含まれています。数千の定数を持つ途方もなく巨大な列挙型があったとしても、パフォーマンスの影響はAndroidフレームワーク自体の膨らみの横では無視できるほど確実です。
Kevin Krumwiede 2017年

57

enumに値があるだけの場合は、次に示すように、IntDef / StringDefを使用する必要があります。

https://developer.android.com/studio/write/annotations.html#enum-annotations

例:の代わりに:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

あなたが使う:

@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}

public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;

そして、それをパラメータ/戻り値として持つ関数では、次を使用します:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

列挙型が複雑な場合は、列挙型を使用してください。それは悪いことではありません。

列挙型と定数値を比較するには、ここを読む必要があります:

http://hsc.com/Blog/Best-Practices-For-Memory-Optimization-on-Android-1

それらの例は、2つの値を持つ列挙型です。定数整数を使用する場合の128バイトと比較して、dexファイルでは1112バイトかかります。列挙型はC / C ++での動作とは対照的に、実際のクラスであるため、理にかなっています。


列挙型の長所と短所を提供する他の回答に感謝しますが、この回答は、特にAndroidに最適なソリューションを提供するため、受け入れられる回答になるはずです。サポートアノテーションを使用する方法です。私たちは、Android開発者が「列挙型を使用する強い理由がない限り、アノテーション付きの定数を使用する」と考えるべきです。 enumを使用できます。」後でパフォーマンスの問題が発生しないようにしてください。
w3bshark 2016

3
@ w3bsharkパフォーマンスの問題が発生している場合、列挙型を解決するために最初に検討する必要があるのは列挙型ではありません。注釈には独自のトレードオフがあります。それらにはコンパイラサポートがないため、独自のフィールドやメソッドを持つことができず、書くのが面倒であり、intに注釈を付けることを忘れがちです。これらは全体的に優れたソリューションではなく、必要なときにヒープ領域を節約するためだけに存在します。
Malcolm

1
@androiddeveloper enum宣言で定義されていない定数を渡しても、間違いはありません。このコードはコンパイルされません。IntDefアノテーション付きで間違った定数を渡すと、渡されます。時計についてもかなりはっきりしていると思います。より多くのCPUパワー、RAM、およびバッテリーを備えたデバイス:電話または時計?そのため、パフォーマンスのためにソフトウェアをさらに最適化する必要があるのはどれですか。
Malcolm

3
@androiddeveloper OK、厳密な用語を主張すると、間違いとして、私はコンパイル時エラーではないエラー(ランタイムエラーまたはプログラムロジックエラー)を意味します。あなたは私を荒らしていますか、それともそれらとコンパイル時エラーの利点との違いがわかりませんか?これはよく議論されたトピックです。これをWebで調べてください。時計に関しては、Androidにはより多くのデバイスが含まれていますが、最も制約のあるものは最も一般的な分母になります。
Malcolm

2
@androiddeveloper私はすでにこれらの両方のステートメントに応答しました。もう一度繰り返しても、私が言ったことは無効になりません。
Malcolm

12

以前の回答に加えて、Proguardを使用している場合(そして、サイズを削減してコードを難読化するために間違いなく実行する必要があります)、可能な場合はEnums自動的に変換され@IntDefます:

https://www.guardsquare.com/en/proguard/manual/optimizations

クラス/ボックス化解除/列挙型

可能な場合は常に、列挙型を整数定数に単純化します。

したがって、いくつかの離散値があり、いくつかのメソッドがこの値のみを取り、同じタイプの他の値をEnum取り込めない場合は、を使用します。これは、Proguardがコードを最適化するこの手動作業を行うためです。

そして 、ジェイクウォートンの列挙型の使用に関する良い投稿があります。ぜひご覧ください

ライブラリ開発者として、消費するアプリのサイズ、メモリ、パフォーマンスにできるだけ影響を与えたくないため、これらの小さな最適化を行う必要があることを認識しています。しかし、パブリックAPIに列挙型を配置することと、整数値を適切な場所に配置することは、まったく問題がないことを理解することが重要です。情報に基づいた決定を行うための違いを知ることが重要です


11

Androidでの列挙型の使用は厳密に避けるべきですか?

いいえ。「厳密に」とは、それらが非常に悪いことを意味します。まったく使用しないでください。列挙型(uiスレッドで連続)を使用した非常に多くの(数千または数百万の)操作のような極端な状況では、パフォーマンスの問題が発生する可能性があります。より一般的なのは、バックグラウンドスレッドで厳密に発生するはずのネットワークI / O操作です。オブジェクトがあるかどうか-列挙型の最も一般的な使用法は、おそらく型チェックのいくつかの種類であるこのまたはそのとても速くあなたが列挙型の単一の比較と整数の比較との違いに気付くことができませんです。

誰かがAndroidで同じことを表すための最良の方法を明確にできますか?

これには一般的な経験則はありません。自分に合ったものを何でも使用して、アプリを準備してください。後で最適化する-気づいた後、アプリの一部の側面を遅くするボトルネックが発生しています。


1
サポートアノテーションで定数を使用して今すぐ最適化するのと同じくらい簡単なのに、なぜ後で最適化するのですか?こちらで@android_developerの回答をご覧ください。
w3bshark 2016

11

Android Pでは、Googleは列挙型の使用に制限/異論はありません

ドキュメントは、以前は慎重になることが推奨されていた場所が変更されましたが、現在は触れられていません。 https://developer.android.com/reference/java/lang/Enum


1
これ以外に他の証拠はありますか:私はそれについての@JakeWhartonステートメントを見つけました:twitter.com/jakewharton/status/1067790191237181441。誰でもバイトコードを確認できます。
soshial

4

2つの事実。

1、列挙型は、JAVAの最も強力な機能の1つです。

2、Androidの携帯電話は通常、たくさんのメモリを持っています。

だから私の答えはNOです。AndroidではEnumを使用します。


2
Androidに多くのメモリがある場合でも、アプリがすべてを消費し、他のユーザーに何も残さないようにする必要はありません。アプリをAndroid OSに殺されないようにするには、ベストプラクティスに従う必要があります。列挙型を使用しないと言っているのではなく、代替手段がない場合にのみ使用してください。
Varundroid、2017年

1
ベストプラクティスに従うべきだと私は同意しますが、ベストプラクティスが常に最小のメモリを使用することを意味するわけではありません。コードをクリーンでわかりやすい状態に保つことは、数kのメモリを節約するよりもはるかに重要です。バランスを見つける必要があります。
Kai Wang

1
バランスを見つける必要があるが、TypeDefを使用してもコードの見栄えが悪くなったり、保守性が低下したりすることには同意します。私は1年以上使用していますが、コードが理解しにくいと感じたことは一度もありません。また、列挙型に比べてTypeDefsが理解しにくいと不平を言う仲間はいません。私のアドバイスは、他に選択肢がない場合にのみ列挙型を使用することですが、可能であればそれを避けてください。
Varundroid、2017年

2

追加したいのは、キーまたは値がアノテーションインターフェースの1つであるList <>またはMap <>を宣言するときに@Annotationsを使用できないことです。「ここでは注釈は許可されていません」というエラーが表示されます。

enum Values { One, Two, Three }
Map<String, Values> myMap;    // This works

// ... but ...
public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;

@Retention(RetentionPolicy.SOURCE)
@IntDef({ONE, TWO, THREE})
public @interface Values {}

Map<String, @Values Integer> myMap;    // *** ERROR ***

したがって、リスト/マップにパックする必要がある場合は、enumを使用します。追加できるので、@ annotated int / stringグループは追加できません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.