Mendapatkan enum yang terkait dengan nilai int

89

Sebelumnya, saya memiliki enum LegNo saya yang didefinisikan hanya sebagai:

NO_LEG, LEG_ONE, LEG_TWO

dan dengan menelepon return LegNo.values()[i];, saya bisa mendapatkan nilai yang terkait dengan setiap enum.

Tapi sekarang saya telah memutuskan saya ingin LegNoenum NO_LEGmenjadi int -1 daripada 0 jadi saya memutuskan untuk menggunakan konstruktor pribadi untuk menginisialisasi dan mengatur nilai intnya

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

satu-satunya hal sekarang adalah karena saya melakukannya dengan cara ini, values()metode tersebut tidak akan berfungsi untuk NO_LEGenum. Bagaimana cara mendapatkan enum yang terkait dengan int? Apakah ada cara yang efisien untuk melakukan ini selain menggunakan pernyataan case switch atau if-elseif-elseif

Saya dapat melihat banyak pertanyaan SO yang harus dilakukan dengan mendapatkan nilai int dari enum, tapi saya mengejar sebaliknya.

L-Samuels
sumber

Jawaban:

148

EDIT Agustus 2018

Hari ini saya akan menerapkan ini sebagai berikut

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

    LegNo(int value) {
        this.value = value;
    }

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

Anda harus mempertahankan pemetaan di dalam enum.

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

Blok statis hanya akan dipanggil sekali, jadi praktis tidak ada masalah kinerja di sini.

EDIT: Mengganti nama metode menjadi valueOfkarena lebih sejalan dengan kelas Java lainnya.

adarshr
sumber
maaf saya tidak yakin apakah saya cukup jelas. saya ingin meneruskan int dan mendapatkan enum yang terkait dengannya.
L-Samuels
@ L-Samuels Kurasa saya tidak membaca pertanyaan Anda dengan benar. Lihat pembaruan saya.
adarshr
2
Saya tahu ini tampak jelas, tetapi menggunakannya suka begitu: LegNo foo = LegNo.valueOf(2);. Kode sebelumnya akan mengembalikan file LegNo.LEG_TWO.
FirstOne
1
Untuk diperhatikan, meneruskan nilai integer yang tidak valid (tidak dipetakan) akan kembali null, seperti yang diharapkan dengan menggunakan HashMap.get : Mengembalikan nilai yang dipetakan kunci tertentu, atau null jika peta ini tidak berisi pemetaan untuk kunci tersebut.
FirstOne
Meskipun sintaks streaming rapi, perlu ditunjukkan bahwa ini memiliki kompleksitas waktu yang lebih tinggi daripada peta statis (yang memang memiliki konsumsi memori yang lebih tinggi). Bukan masalah untuk 3 nilai, tapi pasti menjadi perhatian jika Anda valueOf()menggunakan enum 1000 anggota di dalam loop lain.
Patrick M
24

Anda juga bisa menyertakan metode statis dalam enum yang mengulangi semua anggota dan mengembalikan yang benar.

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

Sekarang, jika Anda ingin mendapatkan nilai Enum dengan integer, Anda cukup menggunakan:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);
Mike Adler
sumber
Saya kira ini akan lebih elegan daripada menggunakan pernyataan if else if. Tetapi mengingat ada lebih banyak enum untuk pencarian maka strategi peta yang disarankan oleh @adarshr akan lebih baik. Meskipun memilih humor.
L-Samuels
1
Saya juga sangat menyukai strategi peta. Terutama ketika enum memiliki banyak nilai atau harus sering dicari melalui mekanisme ini. Namun, jika mencari nilai dengan int terkait adalah kejadian yang relatif jarang atau Anda memiliki banyak enum berbeda dengan persyaratan pencarian yang sama, saya yakin cara saya akan lebih ramah sumber daya, karena biaya overhead untuk peta disimpan. Plus, saya merasa itu membuat kode tidak terlalu berantakan. Saya memiliki beberapa kasus penggunaan di mana saya pasti akan beralih ke tipe Peta sendiri.
Mike Adler
Anda tidak boleh mendapatkan nilai enum terkait dengan ordinalnya. Menggunakan peta statis adalah metodologi yang direkomendasikan oleh arsitek Java.
hfontanez
Bidang legIndex bertepatan dengan ordinal dalam contoh ini, tetapi bisa berupa nilai int apa pun. Tidak ada pencarian ordinal dilakukan. Selain itu, berikan atau tautkan alasan mengapa menurut Anda pencarian ordinal buruk.
Mike Adler
1
"Kaki tidak ditemukan. Diamputasi?"
Gnagy
17

jawaban adarshr disesuaikan dengan Java 8:

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}
Marcin
sumber
11

Anda juga dapat mengakses nilai Enum yang sesuai dengan nilai integer yang diberikan hanya dengan memanggil metode values ​​() pada enum LegNo. Ini mengembalikan bidang enum LegNo: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Bukan persis hal yang dia cari, tetapi cukup dekat dan sangat sederhana. (Meskipun subjek sudah mati, ini mungkin berguna untuk orang lain.)

Tadeas
sumber
6

Cara Java 8 dengan nilai default:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

memanggil:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

atau dengan Pengecualian:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});
Dmitry Sokolyuk
sumber
2
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}
Andrey Lebedenko
sumber
1

Karena enum Anda hanya berisi 3 elemen, cara tercepat adalah menggunakan serangkaian if else, seperti yang Anda sarankan.

edit: jawaban yang diberikan adarshr lebih cocok untuk kasus umum, di mana ada banyak nilai enum, tapi saya pikir itu berlebihan untuk masalah Anda.

DieterDP
sumber
Memiliki Mapkode di kode Anda tentu tidak berlebihan. Selain itu membuat metode ini jauh lebih bersih daripada spageti dengan kondisi lain.
adarshr
Saya setuju bahwa Peta lebih baik setelah Anda memiliki banyak nilai enum, tetapi untuk 3 nilai saya akan tetap menggunakan konstruksi if / else. Kurasa ini masalah selera.
DieterDP
Apapun pendekatan yang kita pilih, tanda tangan metode public LegNo valueOf(int value)tidak boleh diubah. If-else kemudian bisa ditulis di dalam enum itu sendiri. Jika if-else keluar dari enum, maka itu pasti menjadi kode yang tidak terlalu bersih.
adarshr
1
Saya setuju dengan Anda sepenuhnya :)
DieterDP
1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null
Tom B
sumber
4
Dapat diterima untuk enum dengan nilai <10 tetapi sama sekali tidak efektif untuk sejumlah besar nilai enum karena kerumitan pencarian O (n)
Alfishe