英文:
How to use generics to simplify my Search Method?
问题
在我的程序中,我正在尝试创建一个方法,该方法将搜索一个BaseContact的ArrayList。BaseContact是一个抽象类,它在PersonContact和BusinessContact类中进行了扩展。我想创建一个搜索方法,询问用户他们想要按哪个属性搜索(名称、地址、ID号等),然后询问他们要搜索什么(例如:"John"、"123 Oak"、2)。目前,我的代码使用Scanner和switch case来询问用户他们想要按什么进行搜索,然后运行一个for循环来检查他们的输入是否等于具有该属性的任何联系人。我想知道是否有任何方法可以利用泛型或其他方式使代码不那么重复。
public void search() {
Boolean active = true;
@SuppressWarnings("resource")
Scanner sc = new Scanner(System.in);
// While Statement for Search
while (active) {
System.out.println("--- Search ---");
System.out.println(
"\n1. Id\n2. Name\n3. Phone Number\n4. Date of Birth\n5. Hobby\n6. Website URL\n7. Street\n8. City\n9. State\n10. Zip Code");
String choice = sc.nextLine();
// Switch statement
switch (choice) {
case "1":
case "Id":
System.out.println("What is the Id number?");
int id = sc.nextInt();
sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getId() == id) {
System.out.println("Contact with the Id of " + id);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "2":
case "Name":
System.out.println("What is the Name?");
String name = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getName().equals(name)) {
System.out.println("Contact with the Name of " + name);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "3":
case "Phone Number":
System.out.println("What is the Phone Number?");
String phoneNum = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getPhoneNum().equals(phoneNum)) {
System.out.println("Contact with the Phone Number of " + phoneNum);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
// 以下省略其他选项的代码...
// ...
case "Exit":
System.out.println("--- EXIT Search ---");
active = false;
break;
default:
boolean exit = true;
while (exit) {
System.out.println("INVALID INPUT\nWould you like to Exit? Y/N");
String Cexit = sc.nextLine();
if (Cexit.equalsIgnoreCase("Y")) {
System.out.println("--- EXIT ---");
exit = false;
active = false;
}
if (Cexit.equalsIgnoreCase("N")) {
exit = false;
}
}
break;
} // End Switch Statement
} // End while loop
}
这是您提供的代码的翻译部分。如果您有其他问题或需要进一步的帮助,请随时告诉我。
英文:
In my program, I am trying to create a method that will Search through an ArrayList of BaseContact. BaseContact is an abstract class that is extended within a PersonContact and BusinessContact Classes. I want to create a Search method that asks the user what property they want to search by (Name, address, ID number etc.) and then what they want to search (ex. "John", "123 Oak", 2). Currently, my code is using a Scanner and switch case to ask the user what they want to search by and then runs a for loop to check if their input is equal to any of the contacts with that property. I was wondering if there is any way to utilize Generics or something else to make the code less repetitive.
public void search() {
Boolean active = true;
@SuppressWarnings("resource")
Scanner sc = new Scanner(System.in);
// While Statement for Search
while (active) {
System.out.println("--- Search ---");
System.out.println(
"\n1. Id\n2. Name\n3. Phone Number\n4. Date of Birth\n5. Hobby\n6. Website URL\n7. Street\n8. City\n9. State\n10. Zip Code");
String choice = sc.nextLine();
// Switch statement
switch (choice) {
case "1":
case "Id":
System.out.println("What is the Id number?");
int id = sc.nextInt();
sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getId() == id) {
System.out.println("Contact with the Id of " + id);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "2":
case "Name":
System.out.println("What is the Name?");
String name = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getName().equals(name)) {
System.out.println("Contact with the Name of " + name);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "3":
case "Phone Number":
System.out.println("What is the Phone Number?");
String phoneNum = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getPhoneNum().equals(phoneNum)) {
System.out.println("Contact with the Phone Number of " + phoneNum);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "4":
case "Date of Birth":
System.out.println("What is the Date of Birth? (Month Day, Year)");
String dob = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getType() == "personContact") {
PersonContact temp = (PersonContact) contact;
if (dob.equals(temp.getDob())) {
System.out.println("Contact with the Date of Birth of " + dob);
System.out.println("Contact- " + contact.toString() + ":\n");
}
}
}
break;
case "5":
case "Hobby":
System.out.println("What is the Hobby?");
String hobby = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getType() == "personContact") {
PersonContact temp = (PersonContact) contact;
if (hobby.equals(temp.getHobby())) {
System.out.println("Contact with the Hobby of " + hobby);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
}
break;
case "6":
case "Website URL":
System.out.println("What is the Website URL?");
String url = sc.nextLine();
for (BaseContact contact : contacts) {
if (contact.getType() == "businessContact") {
BusinessContact temp = (BusinessContact) contact;
if (url.equals(temp.getWebsite())) {
System.out.println("Contact with the Website URL of " + url);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
}
break;
case "7":
case "Street":
System.out.println("What is the Street?");
String street = sc.nextLine();
for (BaseContact contact : contacts) {
if (street.equals(contact.getLocation().getStreet())) {
System.out.println("Contact with the Street of " + street);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "8":
case "City":
System.out.println("What is the City?");
String city = sc.nextLine();
for (BaseContact contact : contacts) {
if (city.equals(contact.getLocation().getCity())) {
System.out.println("Contact with the City of " + city);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "9":
case "State":
System.out.println("What is the State?");
String state = sc.nextLine();
for (BaseContact contact : contacts) {
if (state.equals(contact.getLocation().getState())) {
System.out.println("Contact with the State of " + state);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
case "10":
case "Zip Code":
System.out.println("What is the Zip Code?");
String zip = sc.nextLine();
for (BaseContact contact : contacts) {
if (zip.equals(contact.getLocation().getZipCode())) {
System.out.println("Contact with the Zip Code of " + zip);
System.out.println("Contact- " + contact.toString() + "\n");
}
}
break;
// case "5":
case "Exit":
System.out.println("--- EXIT Search ---");
active = false;
break;
default:
boolean exit = true;
while (exit) {
System.out.println("INVALID INPUT\nWould you like to Exit? Y/N");
String Cexit = sc.nextLine();
if (Cexit.equalsIgnoreCase("Y")) {
System.out.println("--- EXIT ---");
exit = false;
active = false;
}
if (Cexit.equalsIgnoreCase("N")) {
exit = false;
}
}
break;
} // End Switch Statement
} // End while loop
}
答案1
得分: 0
所以,如果您的用户必须添加几乎每个联系人信息,为什么不对所有信息进行扫描,然后创建一个扩展了BaseContact
的QueryContact
呢?
如果您实现了equals()
方法,您可以使用ArrayList的contains()
方法。
无论如何,您可以使用流(streams)并执行类似以下的操作:
contacts.stream()
.filter(con -> con.getAddress().equals(queryContact.getAddress()))
.filter(con -> con.getName().equals(queryContact.getName()))
// ...//等等
.collect(Collectors.toList());
当然,如果您需要实现“类似(like)”搜索,可能会变得复杂。
英文:
So, if your user has to add almost every piece of information of a contact
why don't you scan everything and then create a QueryContact
that extends a BaseContact
?
If you implement the equals()
method you could use the contains()
method of the Arraylist.
In anycase you could use streams and do something like this:
contacts.stream()
.filter(con-> con.getAddress().equals(queryContact.getAddress())
.filter(con-> con.getName().equals(queryContact.getName())
...//etc
.collect(Collectors.toList());
It can get of course tricky if you need to implement a 'like' search.
答案2
得分: 0
通过查看代码,我提出了一个重构的想法,可能对你有所帮助。
由于在switch语句的每个情况下,你都在重复相同的结构。我建议将这部分代码提取出来,并将其转换为一个方法。
private void dataQuery(String requestMessage, String logMessage, Function<BaseContact, String> expressionProvider) {
System.out.println(requestMessage);
String input = sc.nextLine();
for (BaseContact contact : contacts) {
if (input.equals(expressionProvider.apply(contact))) {
System.out.println(logMessage + zip);
System.out.println("Contact" + contact.toString() + "\n");
}
}
}
我希望你可以看到与每个情况中使用的相同结构相同。
名为expressionProvider
的参数是一个遵循函数原则(进行转换)的Function。该函数将接收一个BaseContact,然后返回一个String。在中间,你可以按照需要处理该联系人。
以下是方法dataQuery
的一些调用示例:
case "6":
case "Website URL":
dataQuery("What is the Website URL?", "Contact with the Website URL of ",
baseContact -> {
if (baseContact.getType().equals("buisnessContact")) {
return ((BusinessContact) baseContact).getWebsite();
}
return null; //可以返回null而不抛出NPE(空指针异常)
});
break;
case "7":
case "Street":
dataQuery("What is the Street?", "Contact with the Street of ",
baseContact -> baseContact.getLocation().getStreet());
break;
case "8":
case "City":
dataQuery("What is the City?", "Contact with the City of ",
(baseContact -> baseContact.getLocation().getCity()));
break;
case "9":
case "State":
dataQuery("What is the State?", "Contact with the State of ",
(baseContact -> baseContact.getLocation().getState()));
break;
注意:为了使用这种实现,你应该将联系人列表声明为全局变量,以及扫描器(scanner)。如果你无法这样做,那么你必须通过方法的参数传递它们。
更新:
还可以通过创建一个类似这样的枚举来稍微改进switch case语句:
public enum Option {
NONE("", ""),
ID("Id", "1"),
NAME("Name", "2"),
STREET("Street", "3"),
CITY("City", "4");//你可以继续在这里添加选项!
String firstIdentifier;
String secondIdentifier;
Option(String firstIdentifier, String secondIdentifier) {
this.firstIdentifier = firstIdentifier;
this.secondIdentifier = secondIdentifier;
}
public static Option getOptionByIdentifier(String identifier) {
return Arrays.stream(Option.values())
.filter(option -> option.secondIdentifier.equals(identifier) || option.firstIdentifier
.equals(identifier))
.findAny()
.orElse(NONE);
}
}
然后,switch语句将类似于:
Option option = Option.getOptionByIdentifier(choice);
switch (option) {
case ID:
//选择ID时的代码
break;
case NAME:
//选择NAME时的代码
break;
case STREET:
//选择STREET时的代码
break;
case NONE:
//默认情况,基本上是在输入无效时触发
break;
}
个人而言,我认为使用枚举更加清晰。此外,对于你的特定情况,避免了重复的case调用
英文:
By looking at the code I came up with a refactor that might help you.
Since in every case of the switch statement you are repeating the structure. I would recommend extracting that chunk of code and converting it into a method.
private void dataQuery(String requestMessage, String logMessage, Function<BaseContact, String> expressionProvider) {
System.out.println(requestMessage);
String input = sc.nextLine();
for (BaseContact contact : contacts) {
if (input.equals(expressionProvider.apply(contact))) {
System.out.println(logMessage + zip);
System.out.println("Contact" + contact.toString() + "\n");
}
}
}
I hope you can visualize the same structure that you were using in each case.
The parameter called expressionProvider
is a Function that follows the principle of functions (making a transformation). This function will receive a BaseContact and then returning a String. In the middle, you can work with that contact as your desired.
Here are some calls of the method dataQuery
:
case "6":
case "Website URL":
dataQuery("What is the Website URL?", "Contact with the Website URL of ",
baseContact -> {
if (baseContact.getType().equals("buisnessContact")) {
return ((BusinessContact) baseContact).getWebsite();
}
return null;//can return null without throwing a NPE(Null pointer exception)
});
break;
case "7":
case "Street":
dataQuery("What is the Street?", "Contact with the Street of ",
baseContact -> baseContact.getLocation().getStreet());
break;
case "8":
case "City":
dataQuery("What is the City?", "Contact with the City of ",
(baseContact -> baseContact.getLocation().getCity()));
break;
case "9":
case "State":
dataQuery("What is the State?", "Contact with the State of ",
(baseContact -> baseContact.getLocation().getState()));
break;
Note: In order to use this implementation, you should declare the list of contacts as a global variable, as well as the scanner. If you can not do that, then you have to pass them via parameters of the method.
UPDATE:
It is also possible to improve a little bit that switch case statement by creating an enum like this:
public enum Option {
NONE("", ""),
ID("Id", "1"),
NAME("Name", "2"),
STREET("Street", "3"),
CITY("City", "4");// you can keep adding options here!
String firstIdentifier;
String secondIdentifier;
Option(String firstIdentifier, String secondIdentifier) {
this.firstIdentifier = firstIdentifier;
this.secondIdentifier = secondIdentifier;
}
public static Option getOptionByIdentifier(String identifier) {
return Arrays.stream(Option.values())
.filter(option -> option.secondIdentifier.equals(identifier) || option.firstIdentifier
.equals(identifier))
.findAny()
.orElse(NONE);
}
}
Then the switch statement will look something like:
Option option = Option.getOptionByIdentifier(choice);
switch (option) {
case ID:
//your code when ID is selected
break;
case NAME:
//your code when NAME is selected
break;
case STREET:
//your code when STREET is selected
break;
case NONE:
//default case, basically when the input is trash
break;
}
Personally, I think it just looks cleaner using enums. Also, for your specific case, double case calls are avoided
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论