0

我正在尝试找到一种基于某些规则从列表中过滤掉某些项目的最佳方法。例如我们有

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);
    }
}

非常感谢。

我已经重写了上面的方法,如下所示,看起来更好恕我直言:

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);
    }
}
4

3 回答 3

1

我不认为嵌套的 for 循环特别难看。您正在根据有效的任意标准在列表中的项目之间寻找匹配项,因此您需要将每个条目与每个其他条目进行比较。

您可以考虑的一项改进是将迭代代码与比较逻辑分开。这是您使用谓词的方向。为此,您需要一个 Predicate 接受两个对象而不是一个对象。

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);
    }
} 
于 2013-11-14T14:30:15.740 回答
0

是否只有一种方法可以确定两个人之间的身份?如果是这样,最好通过覆盖“equals”和“hashcode”来封装它。

执行此操作后,您可以采用以下方法之一:

  1. 如果您正在创建Person 实例的集合,并希望确保在多次添加同一个人时仅保留集合中的一个实例 - 使用 Set 接口作为底层集合(可能还有 HashSet 实现)。使用正确的等号和哈希码,该集合将不允许重复。

  2. 如果你一个集合(这意味着你无法控制它的创建,因此不能使用上述方法来验证它是在没有重复的情况下构造的)并且想要过滤掉重复的实例,你可以简单地将它提供给 a 的构造函数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]
    

顺便说一句,如果您预计将来会有多个身份标准,您可以使用此处提出的策略

于 2013-11-14T13:56:34.410 回答
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;
        }
   }
}   
于 2013-11-13T07:40:12.580 回答