java 中的組合模式--list to tree

背景

在項目中,使用到了將菜單數(shù)據(jù)轉(zhuǎn)換為數(shù)格式返回給前端的需求硝岗。從數(shù)據(jù)庫中讀取的數(shù)據(jù)莉掂,是以list格式讀取的,那么如何使用java代碼將list轉(zhuǎn)換為tree 呢默垄?
tree型數(shù)據(jù)結(jié)構(gòu)此虑,類似組合模式,只不過組合的子項是自身

image.png

定義TreeNode 節(jié)點

定義TreeNode節(jié)點數(shù)厕倍,節(jié)點中提供了addChildren/addChild 等方法管理子節(jié)點

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TreeNode {

    private static final long serialVersionUID = 1L;

    private String id;
    private String parentId;
    private String title;
    private String key;
    private String value;
    private Collection<TreeNode> children = new ArrayList<>();

        public void addChild(TreeNode treenode) {
           this.initChildren();
           this.children.add(treenode);
        }

public void addChildren(List<TreeNode> children) {

    this.initChildren();

    Optional.ofNullable(children)
      .ifPresent(c -> this.children.addAll(c));
  }

public void clearChildren() {
    this.initChildren();
    this.children.clear();
  }

private void initChildren() {
    if (this.children == null) {
      this.children = new ArrayList<>();
    }
  }

}

使用遞歸實現(xiàn)

遞歸是循環(huán)list里的數(shù)據(jù)寡壮,從list中讀取該項的所有子項

public static List<TreeNode> listToTree(List<TreeNode> items) {

        // 查出父節(jié)點
    List<TreeNode> rootList = items.stream().filter(c->c.getParentId()==null||c.getParentId()==0L).collect(Collectors.toList());

        // 按 parentid - list 進行分組
    Map<Long, List<TreeNode>> collect =
      items.stream().collect(Collectors.groupingBy(ITreeNode::getParentId));

    toTree(collect,rootList);

    return rootList;

  }


  private static void toTree(Map<Long, List<TreeNode>> collect, List<TreeNode> rootNodeList){
        // 根據(jù)分組情況設(shè)置子節(jié)點
    if(rootNodeList==null || rootNodeList.isEmpty()){
      return ;
    }
    rootNodeList.forEach(c->{
      // 查找子節(jié)點
      List<TreeNode> children = collect.get(c.getId());
      c.addChildren(children);
// 遞歸調(diào)用
      toTree(collect,children);
    });
  }

非遞歸,使用棧

所有遞歸調(diào)用的方法讹弯,都可以使用棧完成

 /**
   * 使用棧替換遞歸<br><br/>
   * 在棧中存儲每一次遞歸方法調(diào)用的參數(shù)<br><br/>
     * 參考快速排序非遞歸實現(xiàn)
   *
   * @param items list  數(shù)組
   * @param <T>   樹節(jié)點
   * @return 樹形列表
   */
  public static List<TreeNode> quickListToTree(List<TreeNode> items) {

    List<T> rootList = items.stream().filter(c -> c.getParentId() == null || c.getParentId().equals(0L)).collect(Collectors.toList());

    // 用一個棧集合代替遞歸的函數(shù)棧
    Stack<List<T>> quickStack = new Stack<>();

    // 整個列表的跟况既,已list形式入棧
    quickStack.push(rootList);

    // 循環(huán)結(jié)束條件,棧為空時
    while (!quickStack.isEmpty()) {
      // 棧頂元素出棧,得到父級元素列表
      List<TreeNode> root = quickStack.pop();
      if (CollectionUtils.isEmpty(root)) {
        break;
      }
      // 根據(jù)父級元素组民,查找所有的下一子級元素信息
      List<TreeNode> childList = items.stream().filter(c -> root.stream().anyMatch(r -> r.getId().equals(c.getParentId())))
        .collect(Collectors.toList());
      // 填充父級的子級元素
      root.forEach(r -> r.addChildren(childList.stream().filter(c -> c.getParentId().equals(r.getId())).collect(Collectors.toList())));
      // 如果下一自己元素為空棒仍,則進行下一輪
      if (!CollectionUtils.isEmpty(childList)) {
        quickStack.push(childList);
      }
    }

    return rootList;

  }

組合模式的意圖

將對象組合成樹結(jié)構(gòu)以表示部分-整體層次結(jié)構(gòu)。 Composite 允許客戶端統(tǒng)一處理單個對象和對象的組合臭胜。
復(fù)合模式讓客戶端以統(tǒng)一的方式處理各個對象莫其。

背景案例

每個句子都由單詞組成,而單詞又由字符組成耸三。這些對象中的每一個都是可打印的乱陡,它們可以在它們之前或之后打印一些東西,例如句子總是以句號結(jié)尾仪壮,單詞之前總是有空格

在軟件中憨颠,復(fù)合模式是一種設(shè)計模式。復(fù)合模式描述了一組對象的處理方式與對象的單個實例相同。
復(fù)合的目的是將對象“組合”成樹結(jié)構(gòu)以表示部分-整體層次結(jié)構(gòu)爽彤。實現(xiàn)復(fù)合模式可以讓客戶統(tǒng)一對待單個對象和組合

代碼案例

以上面的句子為例养盗。這里我們有基類 LetterComposite 和不同的可打印類型 Letter、Word适篙、Sentence 往核。

public abstract class LetterComposite {

  private final List<LetterComposite> children = new ArrayList<>();

  public void add(LetterComposite letter) {
    children.add(letter);
  }

  public int count() {
    return children.size();
  }

  protected void printThisBefore() {
  }

  protected void printThisAfter() {
  }

  public void print() {
    printThisBefore();
    children.forEach(LetterComposite::print);
    printThisAfter();
  }
}

public class Letter extends LetterComposite {

  private final char character;

  public Letter(char c) {
    this.character = c;
  }

  @Override
  protected void printThisBefore() {
    System.out.print(character);
  }
}

public class Word extends LetterComposite {

  public Word(List<Letter> letters) {
    letters.forEach(this::add);
  }

  public Word(char... letters) {
    for (char letter : letters) {
      this.add(new Letter(letter));
    }
  }

  @Override
  protected void printThisBefore() {
    System.out.print(" ");
  }
}

public class Sentence extends LetterComposite {

  public Sentence(List<Word> words) {
    words.forEach(this::add);
  }

  @Override
  protected void printThisAfter() {
    System.out.print(".");
  }
}

組裝

public class Messenger {

  LetterComposite messageFromOrcs() {

    var words = List.of(
        new Word('W', 'h', 'e', 'r', 'e'),
        new Word('t', 'h', 'e', 'r', 'e'),
        new Word('i', 's'),
        new Word('a'),
        new Word('w', 'h', 'i', 'p'),
        new Word('t', 'h', 'e', 'r', 'e'),
        new Word('i', 's'),
        new Word('a'),
        new Word('w', 'a', 'y')
    );

    return new Sentence(words);

  }

  LetterComposite messageFromElves() {

    var words = List.of(
        new Word('M', 'u', 'c', 'h'),
        new Word('w', 'i', 'n', 'd'),
        new Word('p', 'o', 'u', 'r', 's'),
        new Word('f', 'r', 'o', 'm'),
        new Word('y', 'o', 'u', 'r'),
        new Word('m', 'o', 'u', 't', 'h')
    );

    return new Sentence(words);

  }

}

使用

var orcMessage = new Messenger().messageFromOrcs();
orcMessage.print(); // Where there is a whip there is a way.
var elfMessage = new Messenger().messageFromElves();
elfMessage.print(); // Much wind pours from your mouth.

何時使用設(shè)計模式

  • 想要表示對象的部分-整體層次結(jié)構(gòu)
  • 希望客戶端能夠忽略對象組合和單個對象之間的差異∪陆冢客戶端將統(tǒng)一處理復(fù)合結(jié)構(gòu)中的所有對象聂儒。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市丹喻,隨后出現(xiàn)的幾起案子薄货,更是在濱河造成了極大的恐慌,老刑警劉巖碍论,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谅猾,死亡現(xiàn)場離奇詭異,居然都是意外死亡鳍悠,警方通過查閱死者的電腦和手機税娜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來藏研,“玉大人敬矩,你說我怎么就攤上這事〈赖玻” “怎么了弧岳?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長业踏。 經(jīng)常有香客問我禽炬,道長,這世上最難降的妖魔是什么勤家? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任腹尖,我火速辦了婚禮,結(jié)果婚禮上伐脖,老公的妹妹穿的比我還像新娘热幔。我一直安慰自己,他們只是感情好讼庇,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布绎巨。 她就那樣靜靜地躺著,像睡著了一般蠕啄。 火紅的嫁衣襯著肌膚如雪认烁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天介汹,我揣著相機與錄音却嗡,去河邊找鬼。 笑死嘹承,一個胖子當著我的面吹牛窗价,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叹卷,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撼港,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了骤竹?” 一聲冷哼從身側(cè)響起帝牡,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒙揣,沒想到半個月后靶溜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡懒震,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年罩息,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片个扰。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓷炮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出递宅,到底是詐尸還是另有隱情娘香,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布办龄,位于F島的核電站烘绽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏土榴。R本人自食惡果不足惜诀姚,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望玷禽。 院中可真熱鬧赫段,春花似錦、人聲如沸矢赁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撩银。三九已至给涕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背够庙。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工恭应, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耘眨。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓昼榛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親剔难。 傳聞我的和親對象是個殘疾皇子胆屿,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文的主要內(nèi)容: 介紹組合模式 示例 組合模式總結(jié) 源碼分析組合模式的典型應(yīng)用java.awt中的組合模式Java...
    小旋鋒的簡書閱讀 1,030評論 0 4
  • 一、前言 本周參加了第五次設(shè)計模式研討會偶宫,主題是組合(Composite)模式非迹,接下來我們來看看該模式的具體內(nèi)容。...
    天上下橙雨閱讀 178評論 0 0
  • 繼承是is-a的關(guān)系纯趋。組合和聚合有點像憎兽,有些書上沒有作區(qū)分,都稱之為has-a结闸,有些書上對其進行了較為嚴格區(qū)分唇兑,組...
    時待吾閱讀 460評論 0 1
  • 我們知道地球和一些其他行星圍繞著太陽旋轉(zhuǎn),也知道在一個原子中桦锄,有許多電子圍繞著原子核旋轉(zhuǎn)扎附。我曾經(jīng)想象,我們的太陽系...
    yufawu閱讀 780評論 0 4
  • 組合多個對象形成樹形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)结耀。組合模式對單個對象(即葉子對象)和組合對象(即容器...
    lyu571閱讀 493評論 0 1