【问题标题】:Convert hierarchical list to a flat list in java在java中将分层列表转换为平面列表
【发布时间】:2019-05-26 08:29:02
【问题描述】:

我有一个分层的list,如下所示,我想将其转换为平面list

我编写了一个名为convertToFlatList 的方法并使用了它。但是最终结果中缺少一些元素。我做错了什么?

还有比我用来将列表转换为平面列表的方法更好的方法吗?

我添加了一个示例代码和一些类似于我必须在我的场景中使用的对象。最终结果应该是 1, 2, 3, 4, 5, 6, 7

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main
{
  public static void main(String[] args)
  {
    Member memberOne = new Member(1);
    Member memberTwo = new Member(2);
    Member memberThree = new Member(3);
    Member memberFour = new Member(4);
    Member memberFive = new Member(5);
    Member memberSix = new Member(6);
    Member memberSeven = new Member(7);

    memberTwo.setChildren(Arrays.asList(memberThree, memberFour));
    memberFour.setChildren(Arrays.asList(memberFive, memberSix));

    List<Member> memberList = Arrays.asList(memberOne, memberTwo, memberSeven);
    List<Member> flatList = new ArrayList<>();
    List<Member> convertedList = convertToFlatList(memberList, flatList);
    System.out.println(convertedList);
  }

  private static List<Member> convertToFlatList(List<Member> memberList, List<Member> flatList)
  {
    for (Member member : memberList)
    {
      if (member.getChildren() != null)
      {
        convertToFlatList(member.getChildren(), flatList);
      }
      else
      {
        flatList.add(member);
      }
    }
    return flatList;
  }
}

class Member
{
  private List<Member> children;

  private int memberId;

  Member(int memberId)
  {
    this.memberId = memberId;
  }

  List<Member> getChildren()
  {
    return children;
  }

  void setChildren(List<Member> children)
  {
    this.children = children;
  }

  int getMemberId()
  {
    return memberId;
  }

  void setMemberId(int memberId)
  {
    this.memberId = memberId;
  }

  @Override
  public String toString()
  {
    return String.valueOf(this.memberId);
  }
}

【问题讨论】:

  • 你需要打电话给flatList.add(member);,不仅是Member没有孩子

标签: java list recursion


【解决方案1】:

如果Member 有子级,您可以正确地将子级添加到展平列表中,但会错过Member 本身。只需将成员的添加移到 else 块之外添加就可以了:

private static List<Member> 
convertToFlatList(List<Member> memberList, List<Member> flatList)
{
    for (Member member : memberList)
    {
        // Always add the member to flatList
        flatList.add(memeber);

        // If it has children, add them toore
        if (member.getChildren() != null)
        {
            convertToFlatList(member.getChildren(), flatList);
        }
    }
    return flatList;
}

【讨论】:

    【解决方案2】:

    如果您使用 Java 8,只需将此方法添加到 Member 类中:

    public Stream<Member> streamAll(){
        if(getChildren() == null){
            return Stream.of(this);
        }
        return Stream.concat(Stream.of(this), getChildren().stream().flatMap(Member::streamAll));
    }
    

    或者,如果您始终将 children 初始化为空列表,则可以删除空检查:

    public Stream<Member> streamAll(){
        return Stream.concat(Stream.of(this), getChildren().stream().flatMap(Member::streamAll));
    }
    

    然后获取平面列表:

    List<Member> convertedList = memberList.stream()
                                           .flatMap(Member::streamAll)
                                           .collect(Collectors.toList());
    

    【讨论】:

    • 可以说孩子总是初始化为一个空列表。在递归中,它在满足特定条件时终止。你的第二个效果如何?它在那里工作。只是想了解它是如何工作的
    • 如果children是一个空列表,那么getChildren().stream().flatMap(Member::streamAll))返回一个空流,然后Stream.of(this)和一个空流的连接就是Stream.of(this)
    【解决方案3】:

    通过使用 java-8 flatMap 和递归来做到这一点

    convertToFlatList 方法更改为只接受一个参数(即List&lt;Member&gt;

    如果getChildren()不是null,则进行递归,否则返回当前对象

    private static List<Member> convertToFlatList(List<Member> memberList)
      {
           return memberList.stream().flatMap(i->{
              if(Objects.nonNull(i.getChildren())) {
                 return Stream.concat(Stream.of(i), convertToFlatList(i.getChildren()).stream());
              }
              return Stream.of(i);
    
          }).collect(Collectors.toList());
    
      }   
    

    输出:

    [1, 2, 3, 4, 5, 6, 7]
    

    【讨论】:

      【解决方案4】:

      其实这是一个树的层次/递归顺序遍历的问题。以下是问题和解决方案的详细信息:

      https://en.wikipedia.org/wiki/Tree_traversal

      顺便说一下,这是你的问题的遍历(深度优先顺序):

      static class Member {
        List<Member> children = new ArrayList<>();
        int id;
        boolean visit = false;
      }
      public List<Member> printList(Member member) {
        Stack<Member> stack =  new Stack<>();
        List<Member> list =  new ArrayList<>();
        stack.push(member);
        while(!stack.isEmpty()) {
          Member node = stack.pop();
          list.add(node);
          for(Member m: node.children) {
          stack.push(m);
          }
        }
        return list;
      }
      

      【讨论】:

      • @BoristheSpider 我不同意。如果您使用堆栈 (LIFO) 或递归方法,它就是 DFS。如果你使用队列 (FIFO),它就是 BFS。
      • @Ricola 问题不在于遍历和方法,而在于树节点列表。事实上,这个示例代码是深度优先(预购)模式。欲了解更多信息,请参阅en.wikipedia.org/wiki/Tree_traversal#Pre-order
      • 我完全同意,这只是对删除评论的@BoristheSpider 的回应
      猜你喜欢
      • 2010-12-16
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 2012-04-24
      • 1970-01-01
      • 2020-01-26
      • 1970-01-01
      • 2018-02-06
      相关资源
      最近更新 更多