我正在尝试根据某些规则找到从列表中过滤掉某些项目的最佳方法。例如,我们有
public class Person{
String name;
String sex;
String dob;
String contactNo;
Person(String name, String sex, String dob, String contactNo) {
this.name = name;
this.sex = sex;
this.dob = dob;
this.contactNo = contactNo;
}
}
List<Person> persons = Arrays.asList(new Person("Bob", "male", "19800101", "12345"),
new Person("John", "male", "19810101", "12345"),
new Person("Tom", "male", "19820101", "12345"),
new Person("Helen", "female", "19800101", "12345"),
new Person("Jack", "male", "19830101", "12345"),
new Person("Suan", "female", "19850101", "12345"));
我想删除那对具有相同dob和contactNo的男性和女性(在上面的示例中删除Bob和Helen)。我使用嵌套循环实现了如下所示,但它看起来很丑陋。请问有更好的方法吗?我可以实现谓词来做到这一点吗?
public void filterPersons() {
List<Person> filtered = new ArrayList<Person>();
for (Person p: persons) {
boolean pairFound = false;
for (Person t: persons) {
if ((p.sex.equals("male") && t.sex.equals("female")) || (p.sex.equals("female") && t.sex.equals("male"))) {
if (p.dob.equals(t.dob) && p.contactNo.equals(t.contactNo)) {
pairFound = true;
break;
}
}
}
if (!pairFound) {filtered.add(p);}
}
System.out.println("filtered size is: " + filtered.size());
for (Person p: filtered) {
System.out.println(p.name);
}
}
非常感谢。
我已经改写了上面的方法,如下所示,看起来更好imho:
public void testFilter() {
Predicate<Person> isPairFound = new Predicate<Person>() {
@Override public boolean apply(Person p) {
boolean pairFound = false;
for (Person t: persons) {
if ((p.sex.equals("male") && t.sex.equals("female")) ||
(p.sex.equals("female") && t.sex.equals("male"))) {
if (p.dob.equals(t.dob) && p.contactNo.equals(t.contactNo)) {
pairFound = true;
break;
}
}
}
return pairFound;
}
};
Iterable<Person> filtered = Iterables.filter(persons, isPairFound);
for (Person p: filtered) {
System.out.println(p.name);
}
}
答案 0 :(得分:1)
我认为嵌套的for循环并不特别难看。您正在根据有效的任意标准查找列表中项目之间的匹配,因此您需要将每个条目与其他所有条目进行比较。
您可以考虑的一个改进是将迭代代码与比较逻辑分开。这是你使用谓词的地方。要做到这一点,你需要一个带有两个对象而不是一个对象的谓词。
public interface PredicateComparator<T> {
boolean compare(T o1, T o2);
}
您的代码现在看起来像这样
public void filterPersons() {
PredicateComparator<Person> predicate = new PredicateComparator<Person>() {
public boolean compare(Person o1, Person o2) {
// comparison logic in here
}
};
List<Person> filtered = new ArrayList<Person>();
for (Person p : persons) {
for (Person t : persons) {
if (predicate.compare(p, t)) {
filtered.add(p);
}
}
}
System.out.println("filtered size is: " + filtered.size());
for (Person p: filtered) {
System.out.println(p.name);
}
}
答案 1 :(得分:0)
您可以使用hashmap删除重复项。地图中的每个条目都表示
(DOB+ContactNo -> persons index in the original list)
功能
public void filterPersons() {
List<Person> filtered = new ArrayList<Person>(persons); // COPY the whole list
HashMap<String,Integer> map = new HashMap<String,Integer>();
int count=-1;
for (Person p: persons) {
count++;
String g = p.sex;
String g_opp = g.equals("male")? "female":"male";
if(!map.contains(p.dob+p.contactNo+g_opp))
{
// if not exists, add to map
map.put(p.dob+p.contactNo,count+g);
}
else
{
// if duplicate found in map, remove both people from list
filtered.remove(count);
filtered.remove(map.get(p.dob+p.contactNo+g));
// now filtered has 2 less elements, update count
count -= 2;
}
}
}
答案 2 :(得分:0)
是否只有一种方法可以确定两个人之间的身份? 如果是这样,最好通过覆盖'equals'和'hashcode'来封装它。
执行此操作后,您可以采用以下方法之一:
如果您正在创建 Person实例的集合,并希望确保在多次添加同一个人时只保留集合中的单个实例 - 请使用Set接口作为底层集合(可能还有HashSet实现)。正确地使用equals和hashcode,该集合将不允许重复。
如果您给一个集合(意味着您无法控制它的创建,因此无法使用上述方法来验证它是否构造没有重复)并且想要过滤你可以简单地将重复的实例提供给HashSet的构造函数,如下所示:
Collection<Integer> containsRepeatingNumbers = Arrays.asList(1,2,3,4,3,3,3,3);
Set<Integer> alldistincts = new HashSet<>(containsRepeatingNumbers);
System.out.println(alldistincts); //[1, 2, 3, 4]
顺便说一句,如果您预计将来有多个身份标准,您可以使用提出的策略here