İnt değeriyle ilişkilendirilen numaralandırma


89

Daha önce, LegNo numaralarım şu şekilde tanımlanmıştı:

NO_LEG, LEG_ONE, LEG_TWO

ve arayarak return LegNo.values()[i]; , her bir enum ile ilişkili değeri elde edebildim.

Ama şimdi LegNonumaralandırmanın NO_LEG0 yerine int -1 olmasını istediğime karar verdim, bu yüzden int değerini başlatmak ve ayarlamak için özel bir kurucu kullanmaya karar verdim

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

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

şu anda tek şey, bu şekilde yaptığım için values()yöntemin NO_LEGenum için işe yaramayacağıdır . İnt ile ilişkili numaralandırmayı nasıl elde ederim? Bunu yapmanın case switch deyimi veya if-elseif-elseif kullanmaktan başka etkili bir yolu var mı?

Numaralandırmadan int değerini almakla ilgili pek çok SO sorusu görebiliyorum, ancak tam tersinin peşindeyim.

Yanıtlar:


149

DÜZENLEME Ağustos 2018

Bugün bunu şu şekilde uygulayacağım

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();
    }
}

Numaralandırmanın içinde bir eşleme tutmanız gerekecek.

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);
    }
}

Statik blok yalnızca bir kez çağrılacaktır, bu nedenle burada neredeyse hiç performans sorunu yoktur.

DÜZENLEME: Yöntemin valueOfdiğer Java sınıflarıyla daha satır içi olması için yeniden adlandırıldı .


Üzgünüm yeterince açık mıyım emin değilim. int'i geçmek ve onunla ilişkili numaralandırmayı almak istiyorum.
L-Samuels

@ L-Samuels Sanırım sorunuzu doğru okumadım. Güncellememe bakın.
adarshr

2
Bu bariz görünüyor biliyorum ama o kadar gibi kullanın: LegNo foo = LegNo.valueOf(2);. Önceki kod bir LegNo.LEG_TWO.
FirstOne

1
Unutulmamalıdır ki, geçersiz bir tamsayı değerinin (eşlenmemiş) geçirilmesi, HashMap.getnull kullanılarak beklendiği gibi geri dönecektir : Belirtilen anahtarın eşlendiği değeri döndürür veya bu eşleme anahtar için eşleşme içermiyorsa null döndürür.
FirstOne

Akış sözdizimi düzgün olsa da, statik haritadan (kuşkusuz daha yüksek bir bellek tüketimine sahip olan) daha yüksek bir zaman karmaşıklığına sahip olduğuna işaret etmeye değer. 3 değer için bir sorun değil, ancak valueOf()başka bir döngü içinde 1000 üyeli bir numaralandırma yapıyorsanız kesinlikle bir endişe kaynağıdır .
Patrick M

24

Numaralandırmaya tüm üyeler arasında yinelenen ve doğru olanı döndüren statik bir yöntem de dahil edebilirsiniz.

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?");
   }
}

Şimdi, tamsayı ile bir Enum değeri almak istiyorsanız, sadece şunu kullanın:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);

Sanırım bu, if else if ifadesi kullanmaktan daha zarif olurdu. Ancak, araştırılacak daha fazla numaralandırma olduğu düşünüldüğünde @adarshr tarafından önerilen harita stratejisi daha iyi olacaktır. Mizah için oy verse de.
L-Samuels

1
Harita stratejisini de çok seviyorum. Özellikle numaralandırma çok fazla değere sahip olduğunda veya bu mekanizma ile çok sık aranması gerektiğinde. Bununla birlikte, ilişkili int tarafından değerlere bakmak nispeten nadir bir durumsa veya aynı arama gereksinimine sahip çok sayıda farklı numaralandırmanız varsa, haritanın ek yükü kaydedildiği için yolumun daha kaynak dostu olacağına inanıyorum. Ayrıca, kodun daha az dağınık olmasını sağladığını düşünüyorum. Yine de kesinlikle Harita türüne kendim geçeceğim birkaç kullanım durumum var.
Mike Adler

Sırasıyla ilişkili bir enum değerini asla türetmemelisiniz. Statik harita kullanmak Java mimarları tarafından önerilen metodolojidir.
hfontanez

LegIndex alanı, bu örnekteki sıra ile çakışır, ancak herhangi bir int değeri olabilir. Sıralı arama yapılmaz. Ayrıca, lütfen sıralı aramanın kötü olduğunu düşünmenizin nedenini belirtin veya bağlayın.
Mike Adler

1
"Bacak bulunamadı. Kesildi mi?"
Gnagy

17

adarshr'ın cevabı Java 8'e uyarlanmıştır:

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);
    }
}

11

Ayrıca, LegNo numaralandırmasında values ​​() yöntemini çağırarak, verilen tamsayı değerine karşılık gelen Enum değerine de erişebilirsiniz. LegNo numaralandırmalarının alanını döndürür: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Tam olarak aradığı şey değil, ama oldukça yakın ve gerçekten de çok basit. (Konu ölmüş olsa da başkası için faydalı olabilir.)


6

Varsayılan değere sahip Java 8 yolu:

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);
    }
}

aramak:

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

veya İstisna ile:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});

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<>();
  }
}

1

Numaranız yalnızca 3 öğe içerdiğinden, en hızlı yol, if elseönerdiğiniz gibi bir dizi kullanmak olacaktır .

düzenleme: adarshr'ın verdiği yanıt, birçok enum değerinin olduğu genel durumlar için daha uygundur, ancak bunun sizin sorununuz için fazla bir şey olduğunu düşünüyorum.


Bir Having Mapkodunuzda kesinlikle bir overkill değildir. Ayrıca yöntemi, eğer-değilse koşullarının bir spagettisinden çok daha temiz hale getirir.
adarshr

Çok sayıda enum değerine sahip olduğunuzda Haritanın daha iyi olacağını kabul ediyorum, ancak 3 değer için bir if / else yapısına bağlı kalırım. Sanırım bu bir zevk meselesi.
DieterDP

Hangi yaklaşımı seçersek seçelim, yöntem imzası public LegNo valueOf(int value)değiştirilmemelidir. If-else o zaman numaralandırmanın içinde yazılabilir. İf-else sıralamanın dışına çıkarsa, kesinlikle o kadar temiz olmayan bir kod olur.
adarshr

1
Size tamamen katılıyorum :)
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

4
<10 değeri olan numaralandırmalar için kabul edilebilir ancak arama karmaşıklığı nedeniyle çok sayıda enum değeri için tamamen etkisizdir O (n)
Alfishe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.