Bir if yapısı bir işi daha iyi bir zamanda yapabiliyorsa, neden bir anahtar için döndürülecek değeri (bir anahtar için) belirlemek için bir HashMap kullanılmalıdır (işlevlerde)?


9

Geçenlerde büyük bir şirkette çalışırken, oradaki programcıların bu kodlama stilini takip ettiklerini fark ettim:

Giriş A ise 12, giriş B ise 21 ve giriş C ise 45 döndüren bir fonksiyonum olduğunu varsayalım.

Böylece fonksiyon imzasını şöyle yazabilirim:

int foo(String s){
    if(s.equals("A"))      return 12;
    else if(s.equals("B")) return 21;
    else if(s.equals("C")) return 45;
    else throw new RuntimeException("Invalid input to function foo");
}

Ancak kod incelemesinde, fonksiyonu aşağıdaki şekilde değiştirmem istendi:

int foo(String s){
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("A", 12);
    map.put("B", 21);
    map.put("C", 45);
    return map.get(s);
}

İkinci kodun neden ilk koddan daha iyi olduğuna kendimi ikna edemiyorum. İkinci kodun çalışması daha fazla zaman alacaktır.

İkinci kodu kullanmanın tek nedeni, daha iyi okunabilirlik sunması olabilir. Ancak işlev birçok kez çağrılıyorsa, ikinci işlev, çağıran yardımcı programın çalışma süresini yavaşlatmaz mı?

Bunun hakkında ne düşünüyorsun?


4
Üç değer için, bir Harita aşırıya kaçmış switchgibi görünüyor ( daha uygun görünüyor if-else). Ancak bir noktada sorunlu hale gelir. Bir Harita kullanmanın ana avantajı, bir dosya veya tablo vb.
JimmyJames

Yanıtlar:


17

Mesele, hashmap oluşumunu fonksiyonun dışına taşımak ve bir kez (ya da başka türlü olduğundan daha az kez) yapmaktır.

private static final Map<String, Integer> map;
static{
    Map<String, Integer> temp = new HashMap<String, Integer>();
    temp.put("A", 12);
    temp.put("B", 21);
    temp.put("C", 45);
    map = Collections.unmodifiableMap(temp);//make immutable
}

int foo(String s){
    if(!map.containsKey(s))
        throw new RuntimeException("Invalid input to function foo");

    return map.get(s);
}

Ancak java7'den bu yana anahtarlarda (son) dizeler elde edebildi:

int foo(String s){
    switch(s){
    case "A":
        return 12;
    case "B": 
        return 21;
    case "C": 
        return 45;
    default: throw new RuntimeException("Invalid input to function foo");
}

1
Bunun OP sorusuna nasıl cevap verdiğini göremiyorum, yani -1 orada. Ama +1 önermek için geçiş.
user949300

Gerçekten mantıklı olmak ve muhtemelen performansı artırmak için kodlama stilinin nasıl doğru bir şekilde uygulanacağını gösterir. 3 seçenek için hala mantıklı değil, ancak orijinal kod muhtemelen çok daha uzundu.
Florian F

12

İkinci örneğinizde, Mapgereksiz başlatma ek yükünü önlemek için bu özel statik üye olmalıdır.

Büyük miktarlarda değerler için harita daha iyi performans gösterir. Bir hashtable kullanarak, yanıt sürekli olarak aranabilir. Birden fazla if yapısı doğru cevabı bulana kadar girdiyi olasılıkların her biri ile karşılaştırmalıdır.

Başka bir deyişle, harita arama O (1) iken, ifs O (n) 'dir, burada n olası girişlerin sayısıdır.

Harita oluşturma O (n) 'dir, ancak statik sabit durum ise yalnızca bir kez yapılır. Sık sık yapılan bir arama için, harita if, program başlarken (veya dile bağlı olarak sınıf yüklenir) biraz daha fazla zaman harcayarak, uzun vadede ifadelerden daha iyi performans gösterecektir .

Bununla birlikte, harita bu iş için her zaman doğru araç değildir . Çok fazla değer olduğunda veya değerlerin bir metin dosyası, kullanıcı girişi veya veritabanı üzerinden yapılandırılabilmesi gerekir (bu durumda harita önbellek görevi görür).


Evet, büyük miktarlarda değerler için harita daha iyi performans gösterecektir. Ancak değerlerin miktarı sabittir ve üçtür.
RemcoGerlich

haritanın oluşturulması O (N) 'dir, sadece içindeki arama O (1)' dir.
Pieter B

İyi puan, cevabımı netleştirdim.

Harita ayrıca performansı çok az etkileyen otomatik kutudan çıkarma gerektirir.
user949300

@ user949300, Java, evet ve sorudaki kod Java gibi görünüyor. Bununla birlikte, herhangi bir dille etiketlenmez ve yaklaşım birden fazla dilde çalışır (ikisi de boks gerektirmeyen C # ve C ++ dahil).

3

Yazılımda iki hız vardır: kodu yazmak / okumak / hata ayıklamak için geçen süre; ve kodun yürütülmesi için geçen süre.

Beni (ve kod gözden geçirenlerinizi) hashmap işlevinin gerçekten if / then / else'dan daha yavaş olduğuna ikna ederseniz (statik bir hashmap yapmak için yeniden düzenleme yaptıktan sonra) VE beni / gözden geçirenleri, sonra devam edin ve hashmap'i if / else ile değiştirin.

Aksi takdirde, hashmap kodu büyük ölçüde okunabilir; ve (muhtemelen) bugfree; bunu sadece ona bakarak hızlıca belirleyebilirsiniz. Eğer gerçekten çalışmadan if / else hakkında aynı şeyi söyleyemezsiniz; yüzlerce seçenek olduğunda fark daha da abartılıyor.


3
Biri bunun yerine bir anahtarla karşılaştırıldığında bu itiraz parçalanıyor ...
Deduplicator

Ayrıca if-ifadelerini tek bir satıra yazarsanız dağılmaktadır.
gnasher729

2
Hashmap oluşumunu başka bir yere koyarak, sadece gerçekte ne olduğunu bulmayı zorlaştırıyorsunuz. İşlevin gerçek etkisinin ne olduğunu bilmek için bu tuşları ve değerleri görmeniz gerekir.
RemcoGerlich

2

HashMap tarzı cevabı çok tercih ederim.

Bunun için bir ölçüm var

Siklomatik Karmaşıklık adı verilen bir kod kalitesi metriği vardır . Bu metrik temel olarak koddaki farklı yolların sayısını sayar ( Siklomatik Karmaşıklık nasıl hesaplanır ).

Her olası yürütme yolu için, bir yöntemin hem doğruluğunu anlamak hem de tamamen test etmek zorlaşır.

Bu, "anahtar kelimeleri kontrol etme" gibi: ifs, elses, whiles, vs. yanlış olabilir boolean testlerinden yararlanır. "Anahtar kelimeleri denetlemek" in tekrar tekrar kullanılması kırılgan kod üretir.

Ek yararlar

Ayrıca, "harita tabanlı yaklaşım", geliştiricileri girdi-çıktı çiftlerini, çalışma zamanında çıkarılabilen, yeniden kullanılabilen, değiştirilebilen, test edilebilen ve doğrulanabilen bir veri kümesi olarak düşünmeye teşvik eder. Örneğin, aşağıda "fo-" ifadesini yeniden yazdım, böylece "A-> 12, B-> 21, C-> 45" e kalıcı olarak kilitlenmeyelim:

int foo(String s){
    HashMap<String, Integer> map = getCurrentMapping();
    return map.get(s);
}

rachet_freak cevabında bu tür bir refactordan bahsediyor, hız ve yeniden kullanım için tartışıyor, çalışma zamanı esnekliği için tartışıyorum (değişmez bir koleksiyon kullanmak duruma bağlı olarak büyük faydalar sağlayabilir)


1
Bu çalışma zamanı esnekliğini eklemek, ileriye yönelik harika bir fikir veya neler olduğunu anlamayı çok daha zorlaştıran gereksiz aşırı mühendisliktir. :-).
user949300

@JimmyJames Bağlantı benim için çalışıyor: Bu: leepoint.net/principles_and_practices/complexity/…
Ivan

1
@ user949300 Mesele şu ki, "foo" yöntemini destekleyen anahtar / değer verisi, bir çeşit netliği hak edebilecek ayrı bir kavramdır . Haritayı yalıtmak için yazmak istediğiniz kod miktarı, büyük ölçüde Haritanın kaç öğeyi içerdiğine ve bu öğelerin ne sıklıkta değiştirilmesi gerektiğine bağlı olacaktır.
Ivan

1
@ user949300 if-else zincirini çıkarmamı önermemin bir nedeni, gruplar halinde if-else zincirlerinin var olduğunu görmem. Benzer roaches türünde, eğer if-else zincirine dayanan bir yöntem varsa, kod tabanında başka / benzer if-else zincirine sahip başka bir yöntem olması muhtemeldir. Benzer bir arama / anahtarlama mantığı yapısı kullanan başka yöntemler olabileceğini varsayarsak, Harita'nın çıkarılması temettü ödeyecektir.
Ivan

Ben de yinelenen gördüm, hemen hemen aynı / başka bloklar her yere dağılmış. İyi koy
Jon Chesterfield

1

Veriler koddan daha iyidir. En azından kod için başka bir dal eklemek çok cazip gelse de, bir tabloya satır eklemek yanlış yapmak zordur. Bu soru bunun küçük bir örneğidir. Bir arama tablosu yazıyorsunuz. Koşullu mantık ve dokümantasyonla tamamlanmış bir uygulama yazın veya tabloyu yazın ve içine bakın.

Bir veri tablosu her zaman bazı verilerin koddan daha iyi bir temsilidir, modulo optimizasyonu geçer. Tablonun ifade edilmesinin ne kadar zor olduğu dile bağlı olabilir - Java bilmiyorum, ancak OP'deki örnekten daha basit bir arama tablosu uygulayabileceğini umuyoruz.

Bu, python'da bir arama tablosu. Bu davetkar bir çatışma olarak görülüyorsa, lütfen sorunun java olarak etiketlenmediğini, yeniden düzenleme dilinin agnostik olduğunu ve çoğu insanın java bilmediğini düşünün.

def foo(s):
    return {
               "A" : 12,
               "B" : 21,
               "C" : 45,
           }[s]

Çalışma zamanını azaltmak için kodu yeniden yapılandırma fikri haklıdır, ancak daha çok ortak kurulumu kaldıran bir derleyici kullanmak istiyorum.


-1 soruya bir cevap değil.
Pieter B

Ne anlamda? Yazarın veri ve kodun ayrı olması gerektiğinde başka bir zinciri bir harita ile değiştirmesini isterdim. Tüm map.put çağrıları talihsiz olsa da, ikinci kodun birinciden daha iyi olmasının açık bir nedeni budur.
Jon Chesterfield

2
@JonChesterfield Bu cevap temelde "daha iyi bir dil kullanın", ki bu nadiren yardımcı olur.
17'de

@ walpen fuar noktası. Ivan, Java ile kabaca aynı gözlemi yaptı, bu yüzden açıkça python'a düşmem gerekmedi. Biraz temizleyip temizleyemeyeceğimi göreceğim
Jon Chesterfield

Bazı eski java kodlarında, çok benzer bir şey için birkaç haritaya ihtiyacım vardı, bu yüzden 2 boyutlu dizilerin N boyutlu dizilerini bir haritaya dönüştürmek için küçük bir yardımcı program yazdım. Biraz hacky ama iyi çalıştı. Bu cevap Python / JS'nin JSONy notasyonunu bu kadar basit ve kolay bir şekilde destekleme gücünü göstermektedir.
user949300
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.