เหตุใดจึงควรใช้ HashMap (ในฟังก์ชั่น) เพื่อกำหนดค่าที่จะส่งคืน (สำหรับคีย์) เมื่อโครงสร้าง if อื่นสามารถทำงานได้ในเวลาที่ดีกว่า


9

ขณะที่ฉันทำงานกับ บริษัท ใหญ่เมื่อไม่นานมานี้ฉันสังเกตเห็นว่าโปรแกรมเมอร์ที่นั่นปฏิบัติตามรูปแบบการเข้ารหัสนี้:

สมมติว่าฉันมีฟังก์ชั่นที่ส่งกลับ 12 ถ้าอินพุตเป็น A, 21 ถ้าอินพุตเป็น B และ 45 ถ้าอินพุตเป็น C

ดังนั้นฉันสามารถเขียนฟังก์ชั่นลายเซ็นเป็น:

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

แต่ในการตรวจสอบรหัสฉันถูกขอให้เปลี่ยนฟังก์ชั่นต่อไปนี้:

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

ฉันไม่สามารถโน้มน้าวใจตัวเองว่าทำไมรหัสที่สองนั้นดีกว่ารหัสแรก รหัสที่สองจะใช้เวลาในการรันแน่นอน

เหตุผลเดียวที่ใช้รหัสที่สองคือสามารถให้อ่านได้ดีขึ้น แต่ถ้าฟังก์ชั่นถูกเรียกหลาย ๆ ครั้งฟังก์ชั่นที่สองจะไม่ทำให้เวลาการเรียกใช้ยูทิลิตี้ช้าลงหรือไม่

คุณคิดอย่างไรเกี่ยวกับเรื่องนี้?


4
สำหรับสามค่าแผนที่ดูเหมือนว่าเกินความเป็นจริง ( switchดูเหมาะสมกว่าif-else) แต่ในบางประเด็นมันก็กลายเป็นปัญหา ข้อได้เปรียบหลักของการใช้แผนที่คือคุณสามารถโหลดได้จากไฟล์หรือตารางเป็นต้นหากคุณเข้ารหัสข้อมูลลงในแผนที่อย่างหนักฉันไม่เห็นคุณค่ามากเกินสวิตช์
JimmyJames

คำตอบ:


16

จุดคือการย้ายการสร้าง hashmap นอกฟังก์ชั่นและทำมันครั้งเดียว (หรือเพียงครั้งน้อยกว่าอย่างอื่น)

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

อย่างไรก็ตาม java มีตั้งแต่ java7 สามารถมีสตริง (สุดท้าย) ในสวิตช์:

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
ฉันไม่เห็นว่าคำตอบนี้จะตอบคำถามได้อย่างไร -1 จึงมี แต่ +1 สำหรับการแนะนำสวิตช์
user949300

มันแสดงให้เห็นถึงวิธีการใช้รูปแบบการเข้ารหัสอย่างเหมาะสมเพื่อให้เหมาะสมและอาจปรับปรุงประสิทธิภาพ มันยังไม่สมเหตุสมผลสำหรับ 3 ตัวเลือก แต่รหัสดั้งเดิมอาจจะนานกว่านั้น
Florian F

12

ในตัวอย่างที่สองของคุณMapควรเป็นสมาชิกแบบสแตติกส่วนตัวเพื่อหลีกเลี่ยงการเริ่มต้นซ้ำซ้อน

สำหรับค่าจำนวนมากแผนที่จะทำงานได้ดีขึ้น การใช้ hashtable สามารถค้นหาคำตอบได้ในเวลาที่กำหนด โครงสร้างแบบหลายตัวหากมีการเปรียบเทียบอินพุตกับแต่ละความเป็นไปได้จนกว่าจะพบคำตอบที่ถูกต้อง

กล่าวอีกนัยหนึ่งการค้นหาแผนที่คือ O (1) ในขณะที่ifs คือ O (n) โดยที่nคือจำนวนอินพุตที่เป็นไปได้

การสร้างแผนที่คือ O (n) แต่จะทำได้เพียงครั้งเดียวหากเป็นสถานะคงที่ สำหรับการค้นหาที่ดำเนินการบ่อยครั้งแผนที่จะมีประสิทธิภาพสูงกว่าifข้อความในระยะยาวโดยเสียค่าใช้จ่ายเพิ่มขึ้นเล็กน้อยเมื่อโปรแกรมเริ่มทำงาน (หรือโหลดคลาสขึ้นอยู่กับภาษา)

ที่ถูกกล่าวว่าแผนที่ไม่ได้มักจะเป็นเครื่องมือที่เหมาะสมสำหรับงานนี้ เป็นเรื่องที่ดีเมื่อมีค่าจำนวนมากหรือต้องกำหนดค่าผ่านไฟล์ข้อความอินพุตของผู้ใช้หรือฐานข้อมูล (ในกรณีที่แผนที่ทำหน้าที่เป็นแคช)


ใช่สำหรับค่าจำนวนมากแผนที่จะทำงานได้ดีขึ้น แต่ปริมาณของค่าคงที่และมันเป็นสาม
RemcoGerlich

การสร้างแผนที่คือ O (N) เฉพาะการค้นหาในนั้นคือ O (1)
ปีเตอร์ B

จุดดีฉันชี้แจงคำตอบของฉัน

แผนที่ยังต้องการการแกะกล่องอัตโนมัติซึ่งมีผลต่อประสิทธิภาพการทำงานเพียงเล็กน้อย
949300

@ user949300 ใน Java ใช่และรหัสในคำถามดูเหมือนจะเป็น Java อย่างไรก็ตามมันไม่ได้ติดแท็กด้วยภาษาใด ๆ และวิธีนี้ใช้ได้กับหลายภาษา (รวมถึง C # และ C ++ ซึ่งไม่ต้องใช้มวย)

3

ซอฟต์แวร์มีความเร็วอยู่สองระดับ: เวลาที่ใช้ในการเขียน / อ่าน / ดีบักโค้ด และเวลาที่ใช้ในการเรียกใช้รหัส

หากคุณสามารถโน้มน้าวฉัน (และผู้ตรวจสอบโค้ดของคุณ) ว่าฟังก์ชั่น hashmap นั้นช้ากว่า if / then / else (หลังจาก refactoring เพื่อทำ hashmap แบบคงที่) และคุณสามารถโน้มน้าวให้ฉัน / ผู้ตรวจสอบว่าเรียกเวลาเพียงพอที่จะทำให้เป็นจริง ความแตกต่างจากนั้นไปข้างหน้าและแทนที่ hashmap ด้วย if / else

มิฉะนั้นรหัส hashmap สามารถอ่านได้อย่างชัดเจน; และ (อาจ) bugfree; คุณสามารถกำหนดได้อย่างรวดเร็วเพียงแค่มองมัน คุณไม่สามารถพูดสิ่งเดียวกันเกี่ยวกับ if / else โดยไม่ได้เรียนรู้ ความแตกต่างนั้นยิ่งเกินจริงเมื่อมีตัวเลือกหลายร้อยตัว


3
ดีคัดค้านที่ตกออกจากกันเมื่อเปรียบเทียบกับสวิทช์แทน ...
Deduplicator

แยกกันถ้าคุณเขียนคำสั่ง if ในบรรทัดเดียว
gnasher729

2
โดยการวางการสร้าง hashmap ที่อื่นคุณเพียงทำให้ยากที่จะทราบว่าเกิดอะไรขึ้นจริง คุณต้องเห็นคีย์และค่าเหล่านั้นเพื่อทราบว่าผลกระทบที่แท้จริงของฟังก์ชันคืออะไร
RemcoGerlich

2

ฉันชอบคำตอบสไตล์ HashMap อย่างมาก

มีตัวชี้วัดสำหรับสิ่งนี้

มีตัวชี้วัดที่มีคุณภาพรหัสเรียกว่าเป็นCyclomatic ซับซ้อน การวัดนี้โดยทั่วไปจะนับจำนวนเส้นทางที่แตกต่างผ่านรหัส ( จะคำนวณความซับซ้อนของ Cyclomatic ) ได้อย่างไร

สำหรับทุกเส้นทางการดำเนินการที่เป็นไปได้วิธีการจะยากขึ้นและยากขึ้นในการทำความเข้าใจและทดสอบความถูกต้องอย่างเต็มที่

มันช่วยลดความจริงที่ว่า "การควบคุมคำหลัก" เช่น: ifs, elses, whiles ฯลฯ ... ยกระดับการทดสอบบูลีนที่อาจผิด การใช้ "การควบคุมคำหลัก" ซ้ำ ๆ จะสร้างรหัสที่เปราะบาง

สิทธิประโยชน์เพิ่มเติม

นอกจากนี้ "แนวทางบนแผนที่" ยังกระตุ้นให้นักพัฒนานึกถึงคู่อินพุต - เอาท์พุตเป็นชุดข้อมูลที่สามารถแยกนำกลับมาใช้ใหม่จัดการที่รันไทม์ทดสอบและตรวจสอบแล้ว ตัวอย่างเช่นฉันเขียน "foo" ด้านล่างเพื่อให้เราไม่ได้ล็อคอย่างถาวรใน "A-> 12, B-> 21, C-> 45":

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

rachet_freak กล่าวถึงประเภทของ refactor ในคำตอบของเขาเขาให้เหตุผลเรื่องความเร็วและการใช้ซ้ำฉันกำลังเถียงกับความยืดหยุ่นในการใช้งานจริง


1
การเพิ่มความยืดหยุ่นในการรันไทม์นั้นเป็นแนวคิดการมองไปข้างหน้าที่ยอดเยี่ยมหรือไม่จำเป็นอย่างยิ่งที่จะทำให้คิดได้ว่าจะเกิดอะไรขึ้น :-)
949300

@JimmyJames ลิงก์ใช้งานได้สำหรับฉัน: มันคือ: leepoint.net/principles_and_practices/complexity/…
Ivan

1
@ user949300 ประเด็นคือข้อมูลคีย์ / ค่าที่สนับสนุนวิธี "foo" เป็นแนวคิดแยกต่างหากที่สามารถทำบุญในรูปแบบของความชัดเจนบางอย่าง จำนวนรหัสที่คุณต้องการเขียนเพื่อแยกแผนที่จะขึ้นอยู่กับจำนวนรายการในแผนที่และจำนวนรายการที่อาจต้องเปลี่ยน
Ivan

1
@ user949300 ส่วนหนึ่งของเหตุผลที่ฉันแนะนำให้ดึงห่วงโซ่ if-else ออกมาคือฉันได้เห็นห่วงโซ่ if-else อยู่ในกลุ่ม ถ้าหากมีวิธีหนึ่งที่อยู่บนพื้นฐานของห่วงโซ่ if-else อาจมีวิธีอื่นที่อื่นในรหัสฐานที่มีห่วงโซ่ if-else ที่เหมือนกัน / เหมือนกัน การแตกแผนที่จะจ่ายเงินปันผลถ้าเราคิดว่าอาจมีวิธีอื่นที่ใช้โครงสร้างตรรกะแบบ look-up / switch-like ที่คล้ายกัน
Ivan

ฉันก็เคยเห็นซ้ำกันเกือบจะเหมือนกันถ้า / บล็อกอื่นกระจายอยู่ทั่วสถานที่ ดีมาก
Jon Chesterfield

1

ข้อมูลดีกว่ารหัส ไม่น้อยเพราะมันเป็นการล่อลวงให้เพิ่มรหัสสาขาอื่นเข้าไป แต่การเพิ่มแถวลงในตารางนั้นยากที่จะเข้าใจผิด คำถามนี้เป็นตัวอย่างเล็ก ๆ ของสิ่งนี้ คุณกำลังเขียนตารางการค้นหา ไม่ว่าจะเขียนการใช้งานสมบูรณ์ด้วยตรรกะเงื่อนไขและเอกสารหรือเขียนตารางออกแล้วค้นหามัน

สารบัญเป็นตัวแทนของข้อมูลที่ดีกว่าการใช้ modulo optimization ความยากของตารางในการแสดงอาจขึ้นอยู่กับภาษา - ฉันไม่รู้ Java แต่หวังว่าจะสามารถใช้ตารางการค้นหาได้ง่ายกว่าตัวอย่างใน OP

นี่คือตารางค้นหาในไพ ธ อน หากสิ่งนี้ถูกมองว่าเป็นการเชิญที่ขัดแย้งกันโปรดพิจารณาว่าคำถามนั้นไม่ได้ติดแท็กจาวาการ refactoring นั้นเป็นผู้ไม่เชื่อเรื่องภาษาและคนส่วนใหญ่ไม่รู้จาวา

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

แนวคิดของการปรับโครงสร้างโค้ดเพื่อลดรันไทม์มีข้อดี แต่ฉันค่อนข้างจะใช้คอมไพเลอร์ที่ใช้การตั้งค่าทั่วไปมากกว่าทำด้วยตัวเอง


-1 ไม่ใช่คำตอบสำหรับคำถาม
ปีเตอร์ B

ในสิ่งที่รู้สึก? ฉันจะขอให้ผู้เขียนแทนที่หากเชนอื่นด้วยแผนที่บนพื้นฐานที่ข้อมูลและรหัสควรแยกจากกัน นี่เป็นเหตุผลที่ชัดเจนว่าทำไมรหัสที่สองดีกว่ารหัสแรกถึงแม้ว่าการเรียก map.put ทั้งหมดจะโชคร้าย
Jon Chesterfield

2
@JonChesterfield คำตอบนี้โดยพื้นฐานแล้ว "ใช้ภาษาที่ดีกว่า" ซึ่งไม่ค่อยมีประโยชน์
walpen

@ จุดยุติธรรม walpen อีวานทำข้อสังเกตอย่างคร่าวๆผ่านทาง Java ดังนั้นฉันจึงไม่จำเป็นต้องไปที่หลาม ฉันจะดูว่าฉันสามารถทำความสะอาดได้นิดหน่อย
Jon Chesterfield

ในโค้ดจาวาที่เก่ากว่านั้นฉันต้องการแผนที่บางอย่างสำหรับสิ่งที่คล้ายกันมากดังนั้นเขียนยูทิลิตี้ตัวน้อยเพื่อแปลงอาร์เรย์ N-dimension ของอาร์เรย์ 2 มิติลงในแผนที่ ค่อนข้างแฮ็ก แต่ทำงานได้ดี คำตอบนี้ชี้ให้เห็นถึงพลังของ Python / JS ในการสนับสนุนสัญกรณ์ JSONy อย่างง่ายดายและง่ายดาย
user949300
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.