Mengapa HashMap harus digunakan (dalam fungsi) untuk menentukan nilai mana yang akan dikembalikan (untuk kunci) ketika konstruk ifay dapat melakukan pekerjaan dalam waktu yang lebih baik?

9

Ketika saya baru-baru ini bekerja di sebuah perusahaan besar, saya perhatikan bahwa para programmer di sana mengikuti gaya pengkodean ini:

Misalkan saya memiliki fungsi yang mengembalikan 12 jika inputnya adalah A, 21 jika inputnya adalah B, dan 45 jika inputnya adalah C.

Jadi saya bisa menulis fungsi tanda tangan sebagai:

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

Tetapi pada ulasan kode saya diminta untuk mengubah fungsi sebagai berikut:

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

Saya tidak bisa meyakinkan diri sendiri mengapa kode kedua lebih baik dari yang pertama. Kode kedua pasti akan membutuhkan lebih banyak waktu untuk dijalankan.

Satu-satunya alasan untuk menggunakan kode kedua adalah karena ia menawarkan keterbacaan yang lebih baik. Tetapi jika fungsi dipanggil berkali-kali maka bukankah fungsi kedua memperlambat waktu menjalankan utilitas menyebutnya?

Apa yang Anda pikirkan tentang ini?

Nikunj Banka
sumber
4
Untuk tiga nilai, Peta sepertinya berlebihan ( switchsepertinya lebih tepat daripada if-else). Tetapi pada titik tertentu, itu menjadi bermasalah. Keuntungan utama menggunakan Peta adalah Anda dapat memuatnya dari file atau tabel, dll. Jika Anda mengkodekan input ke peta, saya tidak melihat banyak nilai di atas sakelar.
JimmyJames

Jawaban:

16

Intinya adalah untuk memindahkan pembuatan hashmap di luar fungsi dan melakukannya sekali (atau hanya kurang dari kali).

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

Namun java sejak java7 dapat memiliki string (final) di switch:

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");
}
aneh ratchet
sumber
1
Saya tidak melihat bagaimana ini menjawab pertanyaan OPs, jadi -1 di sana. Namun +1 memberi saran untuk sakelar.
user949300
Ini menunjukkan bagaimana menerapkan dengan benar gaya pengkodean untuk benar-benar masuk akal dan mungkin meningkatkan kinerja. Itu masih tidak masuk akal untuk 3 pilihan, tetapi kode asli mungkin lebih lama.
Florian F
12

Dalam contoh kedua Anda, Mapharus menjadi anggota statis pribadi untuk menghindari overhead inisialisasi berlebihan.

Untuk sejumlah besar nilai, peta akan berkinerja lebih baik. Dengan menggunakan hashtable, seseorang dapat mencari jawabannya dalam waktu yang konstan. Konstruk multi-jika harus membandingkan input ke setiap kemungkinan hingga menemukan jawaban yang tepat.

Dengan kata lain, pencarian peta adalah O (1) sedangkan ifs adalah O (n) dengan n adalah jumlah input yang mungkin.

Pembuatan peta adalah O (n), tetapi hanya dilakukan sekali jika keadaan konstan statis. Untuk pencarian yang sering dilakukan, peta akan mengungguli ifpernyataan dalam jangka panjang, dengan biaya sedikit lebih banyak saat program dijalankan (atau kelas dimuat, tergantung pada bahasa).

Karena itu, peta tidak selalu merupakan alat yang tepat untuk pekerjaan ini. Sangat menyenangkan ketika ada banyak nilai, atau nilai-nilai harus dapat dikonfigurasi melalui file teks, input pengguna, atau database (dalam hal ini peta bertindak sebagai cache).


sumber
Ya, untuk sejumlah besar nilai, peta akan berkinerja lebih baik. Tetapi jumlah nilainya tetap, dan itu tiga.
RemcoGerlich
pembuatan peta adalah O (N) hanya pencarian di dalamnya adalah O (1).
Pieter B
Poin bagus, saya mengklarifikasi jawaban saya.
Peta ini juga membutuhkan penghapusan otomatis yang mempengaruhi kinerja sangat sedikit.
user949300
@ user949300 di Java, ya, dan kode dalam pertanyaan tersebut tampaknya Java. Namun, ini tidak ditandai dengan bahasa apa pun, dan pendekatan ini berfungsi di berbagai bahasa (termasuk C # dan C ++, yang keduanya tidak memerlukan tinju).
3

Ada dua kecepatan dalam perangkat lunak: waktu yang diperlukan untuk menulis / membaca / men-debug kode; dan waktu yang diperlukan untuk menjalankan kode.

Jika Anda dapat meyakinkan saya (dan pengulas kode Anda) bahwa fungsi hashmap memang lebih lambat daripada if / then / else (setelah refactoring untuk membuat hashmap statis) DAN Anda dapat meyakinkan saya / pengulas bahwa diperlukan waktu yang cukup untuk membuat aktual perbedaan, lalu pergi dan ganti hashmap dengan if / else.

Kalau tidak, kode hash jelas dapat dibaca; dan (mungkin) bugfree; Anda dapat menentukan itu dengan cepat hanya dengan melihatnya. Anda tidak bisa mengatakan hal yang sama tentang if / else tanpa benar-benar mempelajarinya; perbedaannya bahkan lebih besar ketika ada ratusan opsi.

Robert Hanson
sumber
3
Nah, keberatan itu berantakan ketika seseorang membandingkan dengan saklar sebagai gantinya ...
Deduplicator
Juga berantakan jika Anda menulis pernyataan if dalam satu baris.
gnasher729
2
Dengan meletakkan pembuatan hashmap di tempat lain, Anda hanya membuatnya lebih sulit untuk mengetahui apa yang sebenarnya terjadi. Anda perlu melihat kunci dan nilai tersebut untuk mengetahui apa sebenarnya efek fungsi tersebut.
RemcoGerlich
2

Saya sangat suka jawaban gaya HashMap.

Ada metrik untuk ini

Ada metrik kualitas kode yang disebut Kompleksitas Siklomatik . Metrik ini pada dasarnya menghitung jumlah lintasan yang berbeda melalui kode ( cara menghitung Kompleksitas Cyclomatik ).

Untuk setiap jalur eksekusi yang mungkin, metode menjadi semakin sulit untuk dipahami DAN sepenuhnya diuji kebenarannya.

Itu bermuara pada fakta bahwa "mengendalikan kata kunci" seperti: jika, elses, whiles, dll ... meningkatkan tes boolean yang bisa salah. Penggunaan berulang "mengendalikan kata kunci" menghasilkan kode yang rapuh.

Keuntungan tambahan

Juga, "pendekatan berbasis peta" mendorong pengembang untuk memikirkan pasangan input-output sebagai dataset yang dapat diekstraksi, digunakan kembali, dimanipulasi saat runtime, diuji, dan diverifikasi. Misalnya, di bawah ini saya menulis ulang "foo" sehingga kami tidak terkunci secara permanen ke "A-> 12, B-> 21, C-> 45":

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

rachet_freak menyebutkan jenis refactor ini dalam jawabannya, ia berpendapat untuk kecepatan dan penggunaan kembali, saya berpendapat untuk fleksibilitas runtime (walaupun menggunakan koleksi abadi dapat memiliki manfaat besar tergantung pada situasi)

Ivan
sumber
1
Menambahkan bahwa fleksibilitas runtime adalah ide yang baik ke depan, atau terlalu banyak rekayasa yang membuatnya lebih sulit untuk mencari tahu apa yang terjadi. :-).
user949300
@JimmyJames Tautan ini berfungsi untuk saya: Yaitu
Ivan
1
@ user949300 Intinya adalah bahwa data kunci / nilai yang mendukung metode "foo" adalah konsep terpisah yang bisa mendapatkan beberapa bentuk kejelasan. Jumlah kode yang ingin Anda tulis untuk mengisolasi Peta akan sangat bergantung pada berapa banyak item yang ada di Peta dan seberapa sering item-item itu perlu diubah.
Ivan
1
@ user949300 Bagian dari alasan saya menyarankan menarik keluar rantai if-else adalah bahwa saya telah melihat apakah ada rantai di grup. Agak kecoak, jika ada satu metode berdasarkan pada rantai if-else ada kemungkinan metode lain di tempat lain dalam basis kode dengan rantai if-else yang sama / identik. Mengekstrak Peta membayar dividen jika kita berasumsi mungkin ada metode lain yang menggunakan struktur logika look-up / switch-like yang serupa.
Ivan
Saya juga telah melihat duplikat, hampir identik jika / blok lain tersebar di semua tempat. Bagus
Jon Chesterfield
1

Data lebih baik daripada kode. Paling tidak karena terlalu menggoda untuk menambahkan cabang lain ke kode, namun menambahkan baris ke tabel sulit untuk salah. Pertanyaan ini adalah contoh kecil dari ini. Anda sedang menulis tabel pencarian. Entah menulis implementasi, lengkap dengan logika kondisional dan dokumentasi, atau tulis tabel keluar kemudian cari di dalamnya.

Sebuah tabel data selalu merupakan representasi yang lebih baik dari beberapa data daripada kode, optimasi modulo berlalu. Betapa sulitnya tabel untuk diekspresikan mungkin bergantung pada bahasa - Saya tidak tahu Java, tetapi saya berharap Java dapat mengimplementasikan tabel look up lebih sederhana daripada contoh dalam OP.

Ini adalah tabel pencarian dengan python. Jika ini dipandang sebagai konflik yang mengundang, harap pertimbangkan bahwa pertanyaannya bukan tagged java, refactoring adalah agnostik bahasa dan kebanyakan orang tidak tahu java.

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

Gagasan merestrukturisasi kode untuk mengurangi runtime memiliki manfaat, tetapi saya lebih suka menggunakan kompiler yang mengerek pengaturan umum daripada melakukannya sendiri.

Jon Chesterfield
sumber
-1 bukan jawaban untuk pertanyaan.
Pieter B
Dalam arti apa? Saya akan meminta penulis untuk mengganti rantai ifmore dengan peta berdasarkan data dan kode yang harus dipisahkan. Ini adalah alasan yang jelas mengapa kode kedua lebih baik daripada yang pertama, meskipun semua panggilan map.put sangat disayangkan.
Jon Chesterfield
2
@ JonChesterfield Jawaban ini pada dasarnya "menggunakan bahasa yang lebih baik", yang jarang membantu.
walpen
@walpen fair point. Ivan telah melakukan pengamatan yang hampir sama melalui Jawa jadi saya tidak jelas perlu beralih ke python. Saya akan melihat apakah saya bisa sedikit membersihkannya
Jon Chesterfield
Dalam beberapa kode java yang lebih lama, saya memerlukan beberapa peta untuk sesuatu yang sangat mirip, jadi saya menulis sedikit utilitas untuk mengubah array N-dimensi dari array 2-D menjadi peta. Agak hacky tetapi bekerja dengan baik. Jawaban ini menunjukkan kekuatan Python / JS dalam mendukung notasi JSONy begitu sederhana dan mudah.
user949300