混合类型元素的Set迭代异常?Scala代码执行结果不符原因问询
先看你给出的代码:
val signList1 = Set("(", "[", " VIA ") val signList2 = Set('(', '[', " VIA ") val returnStr2 = "xx VIA yy" var returnStr3 = returnStr2 signList2.foreach(e => { val pos = returnStr2.indexOf(e) if (pos == 0) { returnStr3 = returnStr2.substring(pos + 1).trim } else if (pos > 0) { returnStr3 = returnStr2.substring(0, pos).trim } }) println(returnStr3)
你遇到的问题是:用signList1能得到预期的xx,但用signList2时结果还是xx VIA yy,这核心原因是Set的类型不一致导致indexOf方法匹配错误。
具体原因拆解:
signList1的正常逻辑:
这个Set里的元素全是String类型,Scala会自动推断它的类型是Set[String]。遍历的时候,每个e都是String,调用returnStr2.indexOf(e)时,会精准匹配String类的indexOf(String str)方法——这个方法能正确找到" VIA "在"xx VIA yy"中的位置(索引2),因为pos>0,所以执行substring(0,2).trim,得到xx,符合预期。signList2的异常逻辑:
这个Set混合了Char('('、'[')和String(" VIA ")两种类型,Scala会把它的类型推断为Set[Any](或者更精确的Set[Serializable],因为Char和String都实现了这个接口)。当遍历到
" VIA "时,e的静态类型是Any,编译器无法确定它是String,因此不会匹配indexOf(String)方法。反而会去匹配另一个重载:indexOf(int ch)(这个方法原本是用来查找单个字符的,因为JVM中Char以int形式存储)。这时候,
String类型的e会被转换成它的哈希值(int类型),然后去字符串中查找对应Unicode代码点的字符——显然"xx VIA yy"里没有这个字符,所以pos返回-1,不会进入任何修改returnStr3的分支,最终结果还是原字符串。
验证与修正:
你可以在遍历的时候加两行打印,就能直观看到问题所在:
signList2.foreach(e => { println(s"元素类型:${e.getClass.getName},值:$e") val pos = returnStr2.indexOf(e) println(s"找到的索引:$pos") // 原逻辑... })
运行后会看到" VIA "的类型是java.lang.String,但pos是-1,而signList1里的" VIA "会返回正确的索引2。
修正方案很简单:
- 要么统一Set里的元素类型,全部用
String(就像signList1那样); - 如果必须混合类型,就用模式匹配显式处理不同类型:
signList2.foreach { case s: String => val pos = returnStr2.indexOf(s) if (pos == 0) returnStr3 = returnStr2.substring(pos + 1).trim else if (pos > 0) returnStr3 = returnStr2.substring(0, pos).trim case c: Char => val pos = returnStr2.indexOf(c) if (pos == 0) returnStr3 = returnStr2.substring(pos + 1).trim else if (pos > 0) returnStr3 = returnStr2.substring(0, pos).trim case _ => // 忽略其他类型或做对应处理 }
内容的提问来源于stack exchange,提问作者yu.sun

