ฟังก์ชั่นนี้จะถูกเพิ่มเข้าไปในเวอร์ชั่น Java ภายหลังหรือไม่?
บางคนสามารถอธิบายได้ว่าทำไมฉันไม่สามารถทำสิ่งนี้ในขณะที่switch
คำสั่งทางเทคนิคของ Java ทำงานได้หรือไม่
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179
ฟังก์ชั่นนี้จะถูกเพิ่มเข้าไปในเวอร์ชั่น Java ภายหลังหรือไม่?
บางคนสามารถอธิบายได้ว่าทำไมฉันไม่สามารถทำสิ่งนี้ในขณะที่switch
คำสั่งทางเทคนิคของ Java ทำงานได้หรือไม่
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179
คำตอบ:
คำสั่ง Switch กับString
case ถูกนำไปใช้ในJava SE 7อย่างน้อย 16 ปีหลังจากที่ถูกร้องขอครั้งแรก ไม่มีเหตุผลที่ชัดเจนสำหรับการหน่วงเวลา แต่มีแนวโน้มว่าจะเกี่ยวกับประสิทธิภาพ
ขณะนี้คุณลักษณะดังกล่าวได้รับการติดตั้งjavac
ด้วยกระบวนการ "ยกเลิกการชำระเงิน" ไวยากรณ์ที่สะอาดและมีระดับสูงโดยใช้String
ค่าคงที่ในcase
การประกาศจะถูกขยายในเวลาคอมไพล์เป็นรหัสที่ซับซ้อนมากขึ้นหลังจากรูปแบบ รหัสผลลัพธ์ใช้คำแนะนำ JVM ที่มีอยู่เสมอ
A switch
พร้อมString
เคสถูกแปลเป็นสองสวิตช์ระหว่างการคอมไพล์ ครั้งแรกที่จับคู่แต่ละสตริงกับจำนวนเต็มเฉพาะตำแหน่งของมันในสวิตช์เดิม ทำได้โดยการสลับรหัสแฮชของป้ายกำกับก่อน กรณีที่เกี่ยวข้องคือif
คำสั่งที่ทดสอบความเท่าเทียมกันของสตริง; ถ้ามีการชนกันบนกัญชา, if-else-if
การทดสอบเป็นซ้อน สวิตช์ที่สองสะท้อนว่าในซอร์สโค้ดต้นฉบับ แต่แทนที่ฉลากเคสด้วยตำแหน่งที่สอดคล้องกัน กระบวนการสองขั้นตอนนี้ทำให้ง่ายต่อการรักษาการควบคุมการไหลของสวิตช์เดิม
สำหรับความลึกด้านเทคนิคเพิ่มเติมswitch
คุณสามารถอ้างถึงข้อกำหนด JVM ซึ่งการรวบรวมคำสั่งสวิตช์อธิบายไว้ สรุปมีคำแนะนำ JVM ที่แตกต่างกันสองคำที่สามารถใช้สำหรับสวิตช์ได้ขึ้นอยู่กับความแตกต่างของค่าคงที่ที่ใช้โดยเคส ทั้งสองขึ้นอยู่กับการใช้ค่าคงที่จำนวนเต็มสำหรับแต่ละกรณีเพื่อดำเนินการอย่างมีประสิทธิภาพ
หากค่าคงที่หนาแน่นพวกเขาจะใช้เป็นดัชนี (หลังจากลบค่าต่ำสุด) ลงในตารางของพอยน์เตอร์คำสั่ง - tableswitch
คำสั่ง
หากค่าคงที่เบาบางจะทำการค้นหาแบบไบนารีสำหรับกรณีที่ถูกต้องนั่นคือlookupswitch
คำสั่ง
ในการกำจัดวัตถุa switch
บนString
คำแนะนำทั้งสองมีแนวโน้มที่จะใช้ lookupswitch
เหมาะสำหรับสวิทช์ครั้งแรกในรหัสกัญชาเพื่อหาสิ่งที่ตำแหน่งเดิมของคดี tableswitch
ลำดับส่งผลให้เป็นแบบธรรมชาติสำหรับ
คำแนะนำทั้งสองต้องการค่าคงที่จำนวนเต็มที่กำหนดให้แต่ละกรณีเพื่อจัดเรียงในเวลาคอมไพล์ ที่รันไทม์ในขณะที่O(1)
ประสิทธิภาพtableswitch
โดยทั่วไปจะดูดีกว่าO(log(n))
ประสิทธิภาพของlookupswitch
มันก็ต้องมีการวิเคราะห์บางอย่างเพื่อตรวจสอบว่าตารางมีความหนาแน่นเพียงพอที่จะปรับการแลกเปลี่ยนพื้นที่เวลา Bill Venners เขียนบทความที่ยอดเยี่ยมซึ่งครอบคลุมเนื้อหานี้อย่างละเอียดยิ่งขึ้นพร้อมกับการดูคำแนะนำการควบคุมการไหลของ Java อื่น ๆ
ก่อนหน้า JDK 7 enum
สามารถประมาณString
สวิตช์ที่ใช้ วิธีนี้ใช้วิธีสแตติกที่valueOf
สร้างขึ้นโดยคอมไพเลอร์ในทุกenum
ประเภท ตัวอย่างเช่น:
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
Pill
เพื่อดำเนินการบางอย่างตามที่str
ฉันจะยืนยันว่า - อื่นเป็นที่นิยมเพราะมันช่วยให้คุณสามารถจัดการstr
ค่านอกช่วง RED, BLUE โดยไม่ต้องจับยกเว้นvalueOf
หรือตรวจสอบการแข่งขันกับชื่อของตนเอง การแจงนับแต่ละประเภทซึ่งเพิ่งเพิ่มค่าใช้จ่ายที่ไม่จำเป็น จากประสบการณ์ของฉันมันเป็นเพียงความรู้สึกที่จะใช้valueOf
ในการแปลงเป็นการแจงนับหากจำเป็นต้องมีการแสดงค่า typesype ที่เป็นความลับในภายหลัง
(hash >> x) & ((1<<y)-1)
จะให้ค่าที่แตกต่างกันสำหรับสตริงทุกอันที่hashCode
แตกต่างกันและ(1<<y)
น้อยกว่าสองเท่าของจำนวนสตริง (หรือที่ อย่างน้อยก็ไม่ยิ่งใหญ่ไปกว่านั้น)
หากคุณมีสถานที่ในรหัสของคุณที่คุณสามารถเปิดใช้งานสตริงได้ก็ควรจะปรับโครงสร้างสตริงให้ดีขึ้นเพื่อระบุจำนวนของค่าที่เป็นไปได้ซึ่งคุณสามารถเปิดได้ แน่นอนว่าคุณ จำกัด ค่าที่อาจเป็นไปได้ของ Strings ที่คุณสามารถมีได้ในการระบุซึ่งอาจเป็นหรือไม่ต้องการก็ได้
แน่นอนการแจงนับของคุณอาจมีรายการสำหรับ 'อื่น ๆ ' และวิธี fromString (String) จากนั้นคุณอาจมี
ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
case MILK: lap(); break;
case WATER: sip(); break;
case BEER: quaff(); break;
case OTHER:
default: dance(); break;
}
ต่อไปนี้เป็นตัวอย่างที่สมบูรณ์ตามการโพสต์ของ JeeBee โดยใช้ java enum แทนการใช้วิธีการที่กำหนดเอง
โปรดทราบว่าใน Java SE 7 และใหม่กว่าคุณสามารถใช้วัตถุ String ในการแสดงออกของคำสั่งเปลี่ยนแทน
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String current = args[0];
Days currentDay = Days.valueOf(current.toUpperCase());
switch (currentDay) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
System.out.println("boring");
break;
case THURSDAY:
System.out.println("getting better");
case FRIDAY:
case SATURDAY:
case SUNDAY:
System.out.println("much better");
break;
}
}
public enum Days {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
}
สวิตช์ที่ใช้จำนวนเต็มสามารถปรับให้เหมาะกับโค้ดที่มีประสิทธิภาพมาก สวิทช์ที่ยึดตามชนิดข้อมูลอื่นสามารถรวบรวมได้กับชุดคำสั่ง if () เท่านั้น
ด้วยเหตุนี้ C & C ++ จึงอนุญาตเฉพาะการสลับกับประเภทจำนวนเต็มเนื่องจากไม่มีประโยชน์กับประเภทอื่น ๆ
นักออกแบบของ C # ตัดสินใจว่าสไตล์นั้นสำคัญแม้ว่าจะไม่มีประโยชน์ก็ตาม
ผู้ออกแบบของ Java คิดว่าเหมือนนักออกแบบของ C
ตัวอย่างของการString
ใช้โดยตรงตั้งแต่ 1.7 อาจปรากฏขึ้นเช่นกัน:
public static void main(String[] args) {
switch (args[0]) {
case "Monday":
case "Tuesday":
case "Wednesday":
System.out.println("boring");
break;
case "Thursday":
System.out.println("getting better");
case "Friday":
case "Saturday":
case "Sunday":
System.out.println("much better");
break;
}
}
James Curran พูดอย่างย่อ ๆ ว่า: "สวิตช์ที่ใช้จำนวนเต็มสามารถปรับให้เหมาะกับโค้ดที่มีประสิทธิภาพได้มากสวิตช์ที่ใช้ชนิดข้อมูลอื่นสามารถรวบรวมได้เป็นชุดคำสั่ง if () เท่านั้นด้วยเหตุนี้ C & C ++ อนุญาตเฉพาะสวิตช์ที่เป็นประเภทจำนวนเต็มเท่านั้น เพราะมันไม่มีจุดหมายกับประเภทอื่น "
ความคิดเห็นของฉันและเป็นเพียงสิ่งนั้นคือทันทีที่คุณเริ่มเปลี่ยนสิ่งที่ไม่ใช่ภาษาดั้งเดิมคุณจะต้องเริ่มคิดถึง "เท่ากับ" กับ "==" การเปรียบเทียบสองสตริงแรกอาจเป็นขั้นตอนที่ค่อนข้างยาวซึ่งจะเป็นการเพิ่มปัญหาด้านประสิทธิภาพที่กล่าวถึงข้างต้น ประการที่สองหากมีการสลับกับสตริงจะมีความต้องการสำหรับการสลับบนสตริงโดยไม่สนใจขนาดตัวอักษรสลับกับสตริงพิจารณา / ละเว้นสถานที่การเปลี่ยนสตริงตาม regex .... ฉันจะอนุมัติการตัดสินใจที่ประหยัดเวลามากสำหรับ นักพัฒนาภาษาเสียค่าใช้จ่ายเล็กน้อยสำหรับโปรแกรมเมอร์
matched
not matched
(ไม่คำนึงถึงสิ่งต่าง ๆ เช่น [ชื่อ] กลุ่ม / อื่น ๆ )
นอกเหนือจากข้อโต้แย้งที่ดีข้างต้นฉันจะเพิ่มผู้คนจำนวนมากในวันนี้ที่เห็นswitch
ว่าเป็นส่วนที่เหลือล้าสมัยของขั้นตอนที่ผ่านมาของ Java (กลับไปที่ครั้ง C)
ฉันไม่ได้แบ่งปันความคิดเห็นนี้อย่างเต็มที่ฉันคิดว่าswitch
อาจมีประโยชน์ในบางกรณีอย่างน้อยก็เนื่องจากความเร็วของมันและอย่างไรก็ตามมันก็ยังดีกว่าชุดตัวเลขที่เรียงซ้อนที่else if
ฉันเห็นในบางโค้ด ...
แต่จริงๆแล้วมันคุ้มค่าที่จะดูกรณีที่คุณต้องการสวิตช์และดูว่ามันไม่สามารถถูกแทนที่ด้วย OO ที่มากกว่านี้ได้ไหม ตัวอย่างเช่น enums ใน Java 1.5+, บางที HashTable หรือคอลเล็กชันอื่น ๆ (บางครั้งฉันเสียใจที่เราไม่มีฟังก์ชั่น (ไม่ระบุชื่อ) ในฐานะพลเมืองชั้นหนึ่งเช่นใน Lua - ซึ่งไม่มีสวิตช์ - หรือ JavaScript) หรือแม้กระทั่งหลากหลาย
หากคุณไม่ได้ใช้ JDK7 หรือสูงกว่าคุณสามารถใช้hashCode()
เพื่อจำลองมันได้ เพราะString.hashCode()
มักจะส่งกลับค่าที่แตกต่างกันสำหรับสตริงที่แตกต่างกันและมักจะส่งกลับค่าเท่ากับสตริงเท่ากันมันมีความน่าเชื่อถือเป็นธรรม (สตริงที่แตกต่างกันสามารถผลิตกัญชารหัสเดียวกับ @Lii กล่าวถึงในการแสดงความคิดเห็นเช่น"FB"
และ"Ea"
) ดูเอกสาร
ดังนั้นรหัสจะเป็นดังนี้:
String s = "<Your String>";
switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}
int
วิธีการที่คุณจะได้ในทางเทคนิคสลับบน
หรือคุณสามารถใช้รหัสต่อไปนี้:
public final class Switch<T> {
private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);
public void addCase(T object, Runnable action) {
this.cases.put(object, action);
}
public void SWITCH(T object) {
for (T t : this.cases.keySet()) {
if (object.equals(t)) { // This means that the class works with any object!
this.cases.get(t).run();
break;
}
}
}
}
case
มีให้ฉันคิดว่าเป็นค่าคงที่เสมอและString.hashCode()
ไม่ใช่เช่นนั้น (แม้ว่าในการคำนวณการปฏิบัติไม่เคยเปลี่ยนแปลงระหว่าง JVMs)
case
ค่าข้อความไม่ต้องถูกกำหนดในเวลารวบรวมเพื่อให้ทำงานได้อย่างประณีต
เป็นเวลาหลายปีแล้วที่เราได้ใช้ตัวประมวลผลล่วงหน้า (โอเพ่นซอร์ส) สำหรับเรื่องนี้
//#switch(target)
case "foo": code;
//#end
ไฟล์ที่ประมวลผลล่วงหน้ามีชื่อว่า Foo.jpp และประมวลผลเป็น Foo.java ด้วยสคริปต์ ant
ข้อดีคือมันถูกประมวลผลเป็น Java ที่รันบน 1.0 (แม้ว่าโดยทั่วไปแล้วเราจะสนับสนุนกลับไปที่ 1.4 เท่านั้น) นอกจากนี้การทำเช่นนี้ (สวิตช์สตริงจำนวนมาก) ทำได้ง่ายกว่าเมื่อเปรียบเทียบกับ fudging ด้วย enums หรือวิธีแก้ไขอื่น ๆ - รหัสนั้นง่ายกว่ามากในการอ่านบำรุงรักษาและเข้าใจ IIRC (ไม่สามารถให้สถิติหรือเหตุผลทางเทคนิค ณ จุดนี้) มันเร็วกว่า Java เทียบเท่าทั่วไป
ข้อเสียคือคุณไม่ได้แก้ไข Java ดังนั้นจึงมีเวิร์กโฟลว์เพิ่มขึ้นเล็กน้อย (แก้ไขประมวลผลคอมไพล์ / ทดสอบ) และ IDE จะเชื่อมโยงกลับไปที่ Java ซึ่งมีความซับซ้อนเล็กน้อย (สวิตช์กลายเป็นชุดของขั้นตอนตรรกะอื่น / ตรรกะ) และลำดับตัวเรือนสวิตช์ไม่ได้รับการปรับปรุง
ฉันไม่แนะนำสำหรับ 1.7+ แต่มีประโยชน์ถ้าคุณต้องการโปรแกรม Java ที่กำหนดเป้าหมาย JVM ก่อนหน้านี้ (เนื่องจาก Joe สาธารณะไม่ค่อยติดตั้งล่าสุด)
คุณจะได้รับจาก SVNหรือเรียกดูออนไลน์รหัส คุณจะต้องEBuildเพื่อสร้างมันตามที่เป็นอยู่
คำตอบอื่น ๆ ได้กล่าวว่าสิ่งนี้ถูกเพิ่มเข้ามาใน Java 7 และให้วิธีแก้ไขปัญหาสำหรับรุ่นก่อนหน้า คำตอบนี้พยายามที่จะตอบว่า "ทำไม"
Java เป็นปฏิกิริยาต่อความซับซ้อนของ C ++ มันถูกออกแบบมาให้เป็นภาษาที่เรียบง่ายสะอาดตา
String มีการจัดการกรณีพิเศษเล็กน้อยในภาษา แต่สำหรับฉันดูเหมือนว่านักออกแบบพยายามที่จะรักษาปริมาณของปลอกพิเศษและน้ำตาล syntactic ให้น้อยที่สุด
การสลับสายค่อนข้างซับซ้อนภายใต้ประทุนเนื่องจากสตริงไม่ใช่ประเภทดั้งเดิม มันไม่ใช่คุณสมบัติทั่วไปในขณะที่ Java ถูกออกแบบและไม่เหมาะกับการออกแบบที่เรียบง่าย โดยเฉพาะอย่างยิ่งเมื่อพวกเขาตัดสินใจที่จะไม่ใช้ตัวพิมพ์เล็ก == สำหรับสตริงมันจะแปลกไปหน่อยสำหรับกรณีที่ == ไม่ทำงาน
ระหว่าง 1.0 ถึง 1.4 ภาษานั้นค่อนข้างคงเดิม การปรับปรุงส่วนใหญ่ของจาวาอยู่ที่ด้านห้องสมุด
ทุกอย่างเปลี่ยนไปด้วย Java 5 ภาษาได้ถูกขยายออกไปอย่างมาก ส่วนขยายเพิ่มเติมตามมาในรุ่น 7 และ 8 ฉันคาดหวังว่าการเปลี่ยนแปลงทัศนคตินี้ได้รับแรงหนุนจากการเพิ่มขึ้นของ C #
JEP 354: สลับนิพจน์ (ดูตัวอย่าง)ใน JDK-13 และ JEP 361: เปลี่ยนนิพจน์ (มาตรฐาน)ใน JDK-14 จะขยายคำสั่ง switchเพื่อให้สามารถใช้เป็นนิพจน์ได้
ตอนนี้คุณสามารถ:
case L ->
):
รหัสทางด้านขวาของฉลากสวิตช์ "case L ->" ถูก จำกัด ให้เป็นนิพจน์บล็อกหรือ (เพื่อความสะดวก) คำสั่งส่งข้อความ
เพื่อให้ได้ค่าจากนิพจน์สวิตช์,
break
ด้วยคำสั่ง value จะถูกดร็อปไว้ในyield
ข้อความสั่ง
ดังนั้นตัวอย่างจากคำตอบ ( 1 , 2 ) อาจมีลักษณะเช่นนี้:
public static void main(String[] args) {
switch (args[0]) {
case "Monday", "Tuesday", "Wednesday" -> System.out.println("boring");
case "Thursday" -> System.out.println("getting better");
case "Friday", "Saturday", "Sunday" -> System.out.println("much better");
}
ไม่สวยมาก แต่นี่เป็นอีกวิธีสำหรับ Java 6 และซอลเบลโลว์:
String runFct =
queryType.equals("eq") ? "method1":
queryType.equals("L_L")? "method2":
queryType.equals("L_R")? "method3":
queryType.equals("L_LR")? "method4":
"method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
มันเป็นเรื่องง่ายใน Groovy; ฉันฝัง jar Groovy และสร้างgroovy
คลาสยูทิลิตี้เพื่อทำสิ่งเหล่านี้ทั้งหมดและอีกมากมายที่ฉันพบว่าน่ารำคาญใน Java (เนื่องจากฉันติดอยู่กับ Java 6 ในองค์กร)
it.'p'.each{
switch (it.@name.text()){
case "choclate":
myholder.myval=(it.text());
break;
}}...
เมื่อคุณใช้ Intellij ดูที่:
ไฟล์ -> โครงสร้างโครงการ -> โครงการ
ไฟล์ -> โครงสร้างโครงการ -> โมดูล
เมื่อคุณมีหลายโมดูลตรวจสอบให้แน่ใจว่าคุณตั้งค่าระดับภาษาที่ถูกต้องในแท็บโมดูล
public class StringSwitchCase {
public static void main(String args[]) {
visitIsland("Santorini");
visitIsland("Crete");
visitIsland("Paros");
}
public static void visitIsland(String island) {
switch(island) {
case "Corfu":
System.out.println("User wants to visit Corfu");
break;
case "Crete":
System.out.println("User wants to visit Crete");
break;
case "Santorini":
System.out.println("User wants to visit Santorini");
break;
case "Mykonos":
System.out.println("User wants to visit Mykonos");
break;
default:
System.out.println("Unknown Island");
break;
}
}
}