【问题标题】:Java : Recursively Iterating over a mapJava:递归迭代地图
【发布时间】:2016-02-18 02:13:07
【问题描述】:

我们有一个看起来像“递归”数据结构的对象。

假设我们有一个结构如下的 Person 对象

public class Person {
    private String id;

    private Map<String,Person> persons;

    public Person(String id, Map<String,Person> persons){
        this.id = id;
        this.persons = persons;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Map<String, Person> getPersons() {
        return persons;
    }

    public void setPersons(Map<String, Person> persons) {
        this.persons = persons;
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", persons=" + persons + "]";
    }

}

此对象的示例表示形式是:(示例数据)

  Person p1 = new Person("Jim", ImmutableMap.of("A001",new Person("Mike",ImmutableMap.of("D001",new Person("Jack",ImmutableMap.of("E001",new Person("Kim",null))))),
                                                         "Z001",new Person("Adam",ImmutableMap.of("Y001",new Person("Eve",ImmutableMap.of("X001",new Person("Dave",null)))))));

注 1:ImmutableMap 来自 google guava 集合

注意 2:假设 Person 对象中 Map 的“键”是人名。

给定一个人的名字,迭代和获取 id 的最有效方法是什么。

例如,如果输入是'Eve',输出应该是'Y001'

【问题讨论】:

  • 是否允许人们在persons 地图中直接或间接地拥有彼此?
  • 那么到目前为止你尝试过什么?

标签: java algorithm recursion


【解决方案1】:

由于您已经在使用 Guava,请考虑将这样的方法添加到 Person 类:

public FluentIterable<Map.Entry<String, Person>> tree() {
    if (persons == null)
        return FluentIterable.from(ImmutableList.of());
    return FluentIterable.from(persons.entrySet())
            .transformAndConcat(
                entry -> Iterables.concat(ImmutableList.of(entry), entry.getValue().tree()));
}

或者如果你不能使用 Java-8:

public FluentIterable<Map.Entry<String, Person>> tree() {
    if (persons == null)
        return FluentIterable.from(ImmutableList.of());
    return FluentIterable.from(persons.entrySet())
        .transformAndConcat(
            new Function<Entry<String, Person>, Iterable<Entry<String, Person>>>() {
            @Override
            public Iterable<Map.Entry<String, Person>> apply(
                    Map.Entry<String, Person> entry) {
                return Iterables.concat(ImmutableList.of(entry), entry.getValue().tree());
            }
        });
}

它将所有人的树扁平化为单个Iterable,可以在for循环中轻松使用:

String name = "Eve";
for(Map.Entry<String, Person> e : p1.tree()) {
    if(e.getValue().getId().equals(name)) {
        System.out.println(e.getKey());
        break;
    }
}

或者使用FluentIterable 操作:

System.out.println(p1.tree()
                     .firstMatch(e -> e.getValue().getId().equals(name))
                     .get().getKey());

【讨论】:

  • 您知道TreeTraverser 已经为您完成了大部分工作吗?
  • @LouisWasserman,别担心,我已经对你的答案投了赞成票 :-) 它并不比我的短,但确实使用专用功能更好。但是请注意,OP 实际上也需要输入键,因此您的答案实际上并不能解决他的问题。也许你应该编辑它。
【解决方案2】:

这对 Guava 的TreeTraverser 来说是一个完美的工作:

Iterable<Person> allPersons = new TreeTraverser<Person>() {
  @Override public Iterable<Person> children(Person p) {
     return p.getPersons().values();
  }
}.preOrderTraversal(rootPerson);
for (Person p : allPersons) {
  if (p.getId().equals(id)) {
    return p;
  }
}

【讨论】:

    【解决方案3】:

    您的结构实际上是一种树。所以你可以使用标准的树排序方法。如果你的树是一棵二叉树(看起来不是),你也许可以非常有效地做到这一点。

    以下方法可行,但我没有证据证明这是最有效的方法。非二叉树大多只需要插入即可。

    效率取决于您认为递归将堆叠多高。如果你认为大多数结构都会像样本结构一样,即:非常深,但每层只有几个人,这将是相当有效的。尤其是当您的结果接近底部时。

    public String iterate(String name) {
            if (persons != null) {
                for (Entry<String, Person> entry : persons.entrySet()) {
                    Person value = entry.getValue();
                    if (value.getId().equals(name)) {
                        return entry.getKey(); //Since the key of each map is the ID when they return the key
                    } else {
                        String ans = value.iterate(name);
                        if (ans != null) {
                            return ans;
                        }
                    }
                }
            }
            return null;
        }
    

    它可以快速遍历整个人员列表,而无需返回顶部。

    但是,如果您有一个广泛的设置,并且您认为您的值大多会接近顶部,您可以使用类似以下的内容。

    public String iterate(String name) {
            if (persons != null) {
                for (Entry<String, Person> entry : persons.entrySet()) {
                    Person value = entry.getValue();
                    if (value.getId().equals(name)) {
                        return entry.getKey(); //Since the key of each map is the ID when they return the key
                    }
                }
    
                for (Entry<String, Person> entry : persons.entrySet()) {
                    String ans = entry.getValue().iterate(name);
                    if (ans != null) {
                        return ans;
                    }
                }
            }
            return null;
        }
    

    这将允许快速找到靠近结构顶​​部的东西,但会更慢在结构底部找到人。

    我猜你会想要更高的解决方案,它更标准。但是,我不认为它们之间有很大的区别。有关它的另一个版本,请参阅 this question 关于遍历树。

    【讨论】:

      猜你喜欢
      • 2012-08-29
      • 1970-01-01
      • 1970-01-01
      • 2016-09-22
      • 1970-01-01
      • 2012-10-17
      • 2019-12-10
      • 1970-01-01
      • 2022-01-16
      相关资源
      最近更新 更多