使用迭代器模式遍历不同的标准

Traversal on different criteria using iterator pattern

这是问题所在:

假设我有一个巨大的 Person 对象集合,我需要以不同的方式遍历该列表。例如,迭代 1980 年出生或居住在纽约市的人员列表。写下使用 ITERATOR 模式迭代特定年份出生的人的代码。请注意,代码必须易于修改或扩展,以便可以使用不同的标准。

首先我为 Person class 使用构建器模式。

public class Person {
//mandatory
private final String SSN;          //social security number
private final MyDate DOB;          //date of birth
private String name;               //name

//optional
private String phone;              //phone number
private USAddress address;         //home address
private USAddress workAddress;     //work address
private Set<USAddress> FVA;        //frequently visited addresses
private Set<Person> familyMembers; //family members

private Person(PersonBuilder builder){
    ...
}

其次是 Person 的容器 class。

public class PersonList extends ArrayList<Person> {

public PersonList() {
    super();
}

//our list only stores unique elements
@Override
public boolean add(Person person) {
    return !this.contains(person) && super.add(person);
}

//person iterator
public PersonIterator personIterator() {
    return new PersonIterator(this);
}
}

我终于实现了迭代器模式。

public class PersonIterator implements Iterator<Person> {
private int index;
private Mode mode;           //iteration mode
private List<Person> origin; //ref to the input list
private List<Person> temp;   //used to store sublist 

//iteration mode
private enum Mode {
    NORMAL,
    YEAR,
    CITY
}

//constructor, the default mode is normal
public PersonIterator(PersonList list){
    mode = Mode.NORMAL;
    index = 0;
    origin = list;
    temp = new ArrayList<>();
}

//set to normal mode
public void normalMode(){
    index = 0;
    mode = Mode.NORMAL;
}

//set to year mode
public void yearMode(int year){
    index = 0;
    mode = Mode.YEAR;
    temp.clear();
    for(Person p: origin){
        if(p.getDOB().getYear() == year){
            temp.add(p);
        }
    }
}

//set to city mode
public void cityMode(String city){
    index = 0;
    mode = Mode.CITY;
    temp.clear();
    for(Person p: origin){
        if(p.getAddress().getCity().equals(city)){
            temp.add(p);
        }
    }
}

//
@Override
public boolean hasNext() {
    if(mode == Mode.NORMAL){
        return index < origin.size();
    }
    else{
        return index < temp.size();
    }
}

@Override
public Person next() {
    if(mode == Mode.NORMAL){
        if(!this.hasNext()){
            throw new NoSuchElementException();
        }
        Person p = origin.get(index);
        index++;
        return p;
    }
    else{
        if(!this.hasNext()){
            throw new NoSuchElementException();
        }
        Person p = temp.get(index);
        index++;
        return p;
    }
}
}

针对不同的条件,它有多种迭代模式。普通模式只是遍历列表,仅此而已。另一种模式,比如year模式,将所有出生在这一年的人加入到temp子表中,然后正常遍历这个temp子表。

要添加更多模式:

  1. 在枚举中创建模式

  2. 创建一个"xMode"方法,并定义如何填充临时子列表

我测试了植入。

public static void main(String[] args) {

    Person john = new Person.PersonBuilder("110", 1980, 8, 2, "john")
            .phone("120")
            .address("1000", "5th", "ave", "11F", "New York", "NY", 11345)
            .workAddress()
            .addFVA(null)
            .addFamilyMember(null)
            .build();

    Person adam = new Person.PersonBuilder("120", 1980, 11, 22, "adam")
            .phone("130")
            .address("31", "main", "st", "", "Stony Brook", "NY", 11411)
            .workAddress()
            .addFVA(null)
            .addFamilyMember(null)
            .build();

    Person lucy = new Person.PersonBuilder("130", 1978, 1, 3, "lucy")
            .phone("140")
            .address("1200", "6th", "ave", "2A", "New York", "NY", 11346)
            .workAddress()
            .addFVA(null)
            .addFamilyMember(null)
            .build();

    PersonList list = new PersonList();
    list.add(john);
    list.add(adam);
    list.add(lucy);
    PersonIterator i = list.personIterator();
    //normal iterator
    while(i.hasNext()){
        System.out.println(i.next().getName() + " is in the list");
    }

    //year mode, print out persons who were born in 1980
    i.yearMode(1980);
    while(i.hasNext()){
        System.out.println(i.next().getName() + " was born in 1980");
    }

    //city mode, print out persons who live in New York City
    i.cityMode("New York");
    while (i.hasNext()){
        System.out.println(i.next().getName() + " lives in NYC");
    }

这是结果。

john is in the list
adam is in the list
lucy is in the list
john was born in 1980
adam was born in 1980
john lives in NYC
lucy lives in NYC

所以我的问题是:

  1. 我的方法能解决问题吗? 如果不行,请指出更好的方法。

  2. 代码是否易于阅读,易于修改? 如果不行,如何改进?

我认为您的解决方案有点矫枉过正,而且效率不高。 Java 核心 API 已经提供了您需要的大部分内容。所以我有以下建议:

1- 考虑使用 java.util.Set,而不是扩展 ArrayList<Person> 和覆盖唯一性的添加方法。如果您需要保持插入顺序,请使用 LinkedHashSet<Person>,否则只需使用 HashSet<Person>。对集合进行子类化在大多数情况下并不是一个好主意。

2- 您想要实现的只是简单的过滤,这可以使用 Java 8 的流 API 简单而优雅地完成。无需创建自定义迭代器,只需使用流 api 的 filter 操作即可。类似于:

people.stream().filter(person -> person.getBirthYear() == 1980).forEach(person -> System.out.println(person.getName() + " was born in 1980"));

或者如果您想将它们收集到一个新的集合中,比方说,一个新的无序集合:

people.stream().filter(person -> person.getBirthYear() == 1980).collect(Collectors.toSet());

您可以链接多个过滤器:

people.stream().filter(person -> person.getBirthYear() == 1980).filter(person -> "New York".equals(person.getAddress().getCity())).collect(Collectors.toSet());

你可以使用反射

public boolean matchesCriteria(Person p, String value, Object tester) {
    Field f = p.getClass().getDeclaredField(value);
    f.setAccessible(true);
    Object o = f.get(p);
    if(o.equals(tester)) {
        return true;
    }
    return false;
}

然后你可以通过使用 for 循环调用它来切换列表中的所有 Person classes 并检查值

DOB dob = new DOB(//However you create a DOB in the year 1980);
for(Person p : pList) {
    if(matchesCriteria(p, DOB, dob) {
        System.out.println(p.getName(), "Was born in the year 1980");
    }
}

请问return所有1980年出生的人。你可以改变周围的一些东西使其适合方法,或者根据自己的喜好改变方法。

这将获取您的个人 class 中的特定字段,确保(如果它是私有的)使其在 class 之外可访问,并获取其值。然后代码将检查两个对象是否相同,并且 return 布尔值。

例如,如果您有一个标记为 "age" 的字段:(只是为了说明代码如何使用我知道的变量。因为我不知道您是如何创建 MyYear 的)

private int age;

for(Person p : pList) {
    if(matchesCriteria(p, "age", 9) {
        System.out.println(p.getName(), " is 9 year(s) old.");
    }
}

这使得 matchesCriteria(Person, String, Object);对 Person class.

中的任何字段通用