我在接受采访时被问到这个问题,以改进所提供的代码。提供的代码使用了很多if语句,因此我决定使用HashMap
,因为检索会更快。不幸的是,我没有被选中担任该职位。我想知道是否有人知道比我改进代码更好的方法?
/* The following Java code is responsible for creating an HTML "SELECT" list of
U.S. states, allowing a user to specify his or her state. This might be used,
for instance, on a credit card transaction screen.
Please rewrite this code to be "better". Submit your replacement code, and
please also submit a few brief comments explaining why you think your code
is better than the sample. (For brevity, this sample works for only 5
states. The real version would need to work for all 50 states. But it is
fine if your rewrite shows only the 5 states here.)
*/
/* Generates an HTML select list that can be used to select a specific U.S.
state.
*/
public class StateUtils {
public static String createStateSelectList() {
return
"<select name=\"state\">\n"
+ "<option value=\"Alabama\">Alabama</option>\n"
+ "<option value=\"Alaska\">Alaska</option>\n"
+ "<option value=\"Arizona\">Arizona</option>\n"
+ "<option value=\"Arkansas\">Arkansas</option>\n"
+ "<option value=\"California\">California</option>\n"
// more states here
+ "</select>\n"
;
}
/* Parses the state from an HTML form submission, converting it to the
two-letter abbreviation. We need to store the two-letter abbreviation
in our database.
*/
public static String parseSelectedState(String s) {
if (s.equals("Alabama")) { return "AL"; }
if (s.equals("Alaska")) { return "AK"; }
if (s.equals("Arizona")) { return "AZ"; }
if (s.equals("Arkansas")) { return "AR"; }
if (s.equals("California")) { return "CA"; }
// more states here
}
/* Displays the full name of the state specified by the two-letter code. */
public static String displayStateFullName(String abbr) {
{
if (abbr.equals("AL")) { return "Alabama"; }
if (abbr.equals("AK")) { return "Alaska"; }
if (abbr.equals("AZ")) { return "Arizona"; }
if (abbr.equals("AR")) { return "Arkansas"; }
if (abbr.equals("CA")) { return "California"; }
// more states here
}
}
我的解决方案
/* Replacing the various "if" conditions with Hashmap<key, value> combination
will make the look-up in a constant time while using the if condition
look-up time will depend on the number of if conditions.
*/
import java.util.HashMap;
public class StateUtils {
/* Generates an HTML select list that can be used to select a specific U.S.
state.
*/
public static String createStateSelectList() {
return "<select name=\"state\">\n"
+ "<option value=\"Alabama\">Alabama</option>\n"
+ "<option value=\"Alaska\">Alaska</option>\n"
+ "<option value=\"Arizona\">Arizona</option>\n"
+ "<option value=\"Arkansas\">Arkansas</option>\n"
+ "<option value=\"California\">California</option>\n"
// more states here
+ "</select>\n";
}
/* Parses the state from an HTML form submission, converting it to the
two-letter abbreviation. We need to store the two-letter abbreviation
in our database.
*/
public static String parseSelectedState(String s) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("Alabama", "AL");
map.put("Alaska", "AK");
map.put("Arizona", "AZ");
map.put("Arkansas", "AR");
map.put("California", "CA");
// more states here
String abbr = map.get(s);
return abbr;
}
/* Displays the full name of the state specified by the two-letter code. */
public static String displayStateFullName(String abbr) {
{
HashMap<String, String> map2 = new HashMap<String, String>();
map2.put("AL", "Alabama");
map2.put("AK", "Alaska");
map2.put("AZ", "Arizona");
map2.put("AR", "Arkansas");
map2.put("CA", "California");
// more state abbreviations here here
String full_name = map2.get(abbr);
return full_name;
}
}
}
答案 0 :(得分:3)
我认为您的代码存在许多问题,尤其是为每个方法调用重新创建Map
。
我会从一开始就使用接口。我们需要两件事;一个State
和一个StateResolver
。接口看起来像这样:
public interface State {
String fullName();
String shortName();
}
public interface StateResolver {
State fromFullName(final String fullName);
State fromShortName(final String shortName);
Set<? extends State> getAllStates();
}
这允许在稍后阶段(例如数据库)交换实现更合理的内容。但是,让我们坚持使用示例中的硬编码状态。
我会将State
实现为enum
,如此:
public enum StateData implements State {
ALABAMA("Alabama", "AL"),
ALASKA("Alaska", "AK"),
ARIZONA("Arizona", "AZ"),
ARKANSAS("Arkansas", "AR"),
CALIFORNIA("Californiaa", "CA");
private final String shortName;
private final String fullName;
private StateData(final String shortName, final String fullName) {
this.shortName = shortName;
this.fullName = fullName;
}
@Override
public String fullName() {
return fullName;
}
@Override
public String shortName() {
return shortName;
}
}
但是,如上所述,这可以用从数据库加载的bean替换。实施应该是不言自明的。
接下来在解析器上,让我们在enum
:
public final class EnumStateResolver implements StateResolver {
private final Set<? extends State> states;
private final Map<String, State> shortNameSearch;
private final Map<String, State> longNameSearch;
{
states = Collections.unmodifiableSet(EnumSet.allOf(StateData.class));
shortNameSearch = new HashMap<>();
longNameSearch = new HashMap<>();
for (final State state : StateData.values()) {
shortNameSearch.put(state.shortName(), state);
longNameSearch.put(state.fullName(), state);
}
}
@Override
public State fromFullName(final String fullName) {
final State s = longNameSearch.get(fullName);
if (s == null) {
throw new IllegalArgumentException("Invalid state full name " + fullName);
}
return s;
}
@Override
public State fromShortName(final String shortName) {
final State s = shortNameSearch.get(shortName);
if (s == null) {
throw new IllegalArgumentException("Invalid state short name " + shortName);
}
return s;
}
@Override
public Set<? extends State> getAllStates() {
return states;
}
}
这又是自我解释的。变量位于实例级别。 StateData
类的唯一依赖是初始化块。这显然需要为另一个State
实现重写,但这应该不是什么大问题。请注意,如果状态无效,此类将抛出IllegalArgumentException
- 这将需要在某处以某种方式处理。目前还不清楚这会发生什么,但需要考虑的事情。
最后,我们在类
中实现所需的方法public final class StateUtils {
private static final StateResolver STATE_RESOLVER = new EnumStateResolver();
private static final String OPTION_FORMAT = "<option value=\"%1$s\">%1$s</option>\n";
public static String createStateSelectList() {
final StringBuilder sb = new StringBuilder();
sb.append("<select name=\"state\">\n");
for (final State s : STATE_RESOLVER.getAllStates()) {
sb.append(String.format(OPTION_FORMAT, s.fullName()));
}
sb.append("</select>\n");
return sb.toString();
}
public static String parseSelectedState(final String s) {
return STATE_RESOLVER.fromFullName(s).shortName();
}
public static String displayStateFullName(final String abbr) {
return STATE_RESOLVER.fromShortName(abbr).fullName();
}
}
请注意,我们只引用实用程序类顶部的实现,这样可以快速,轻松地交换实现。我们使用static final
引用,StateResolver
只创建一次。我还用基于动态循环的选择替换了选择的硬编码创建。我还使用了一个格式化程序来构建select。
应该注意的是,在Java中构建HTML是一个好主意永远,任何这样做的人都应该对它们做出无法形容的事情。
毋庸置疑,您应该针对上述代码的每一行进行彻底的单元测试。
简而言之,您的答案并没有真正接近适当的,可扩展的企业解决方案。我的解决方案似乎有点矫枉过正,你可能是对的。但我认为这是正确的方法,因为抽象是可重用代码的关键。
答案 1 :(得分:2)
你唯一的错误就是每次重建地图。如果你只构建了一次Map - 也许在构造函数中,我怀疑你会做得很好。
public class StateUtils {
class State {
final String name;
final String abbreviation;
public State(String name, String abbreviation) {
this.name = name;
this.abbreviation = abbreviation;
}
}
final List<State> states = new ArrayList<State>();
{
states.add(new State("Alabama", "AL"));
states.add(new State("Alaska", "AK"));
states.add(new State("Arizona", "AZ"));
states.add(new State("Arkansas", "AR"));
states.add(new State("California", "CA"));
}
final Map<String, String> nameToAbbreviation = new HashMap<String, String>();
{
for (State s : states) {
nameToAbbreviation.put(s.name, s.abbreviation);
}
}
final Map<String, String> abbreviationToName = new HashMap<String, String>();
{
for (State s : states) {
nameToAbbreviation.put(s.abbreviation, s.name);
}
}
public String getStateAbbreviation(String s) {
return nameToAbbreviation.get(s);
}
public String getStateName(String abbr) {
return abbreviationToName.get(abbr);
}
}
答案 2 :(得分:2)
为了避免手动维护2个地图并使它们保持同步,我只想创建第二个地图作为倒置的第一个地图。请参阅here了解如何操作。
另外,如其他人所指出,您只需在方法调用之外创建一次地图。
**只是为了在Scala中做到这一点的方式**
val m = Map("AL" -> "Alabama", "AK" -> "Alaska")
m map { case (k, v) => (v, k) }
// gives: Map(Alabama -> AL, Alaska -> AK)
答案 3 :(得分:2)
每个人似乎都专注于解析,但创作也可以得到改善。获取所有状态名称,按字母顺序对它们进行排序,然后迭代该集合以创建每个option
。这样,用于解析的状态始终与用于波峰的状态同步。如果添加新状态,则只需将其添加到“主”枚举(或其他),两种方法都将反映更改。
答案 4 :(得分:1)
我不喜欢你的代码是你每次调用方法时都会创建一个hashmap。地图应该只在初始化时创建一次,并从方法中引用。
答案 5 :(得分:0)
你做错了什么是人们所说的 - 每次调用方法时你都在创建一个新的HashMap - 一个静态字段可能更适合数据,只有在加载了我的JVM类时才填充它。 / p>
我宁愿在字符串上使用简单的开关 - 搜索并不比HashMap差(至少是渐近),但是你不使用额外的内存。虽然你需要两个长开关 - 更多代码。
但是,与HashMap解决方案相比,后者对我来说也是一样。