All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.hutool.core.map.LinkedForestMap Maven / Gradle / Ivy

Go to download

Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。

There is a newer version: 5.8.34
Show newest version
package cn.hutool.core.map;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * {@link ForestMap}的基本实现。
 *
 * 

该集合可以被视为以{@link TreeEntryNode#getKey()}作为key,{@link TreeEntryNode}实例作为value的{@link LinkedHashMap}。
* 使用时,将每一对键与值对视为一个{@link TreeEntryNode}节点,节点的id即为{@link TreeEntryNode#getKey()}, * 任何情况下使用相同的key都将会访问到同一个节点。
* *

节点通过key形成父子关系,并最终构成多叉树结构,多组平行的多叉树将在当前集合中构成森林。 * 使用者可以通过{@link ForestMap}本身的方法来对森林进行操作或访问, * 也可以在获取到{@link TreeEntry}后,使用节点本身的方法对数进行操作或访问。 * * @param key类型 * @author huangchengxing */ public class LinkedForestMap implements ForestMap { /** * 节点集合 */ private final Map> nodes; /** * 当指定节点已经与其他节点构成了父子关系,是否允许将该节点的父节点强制替换为指定节点 */ private final boolean allowOverrideParent; /** * 构建{@link LinkedForestMap} * * @param allowOverrideParent 当指定节点已经与其他节点构成了父子关系,是否允许将该节点的父节点强制替换为指定节点 */ public LinkedForestMap(boolean allowOverrideParent) { this.allowOverrideParent = allowOverrideParent; this.nodes = new LinkedHashMap<>(); } // ====================== Map接口实现 ====================== /** * 获取当前实例中的节点个数 * * @return 节点个数 */ @Override public int size() { return nodes.size(); } /** * 当前实例是否为空 * * @return 是否 */ @Override public boolean isEmpty() { return nodes.isEmpty(); } /** * 当前实例中是否存在key对应的节点 * * @param key key * @return 是否 */ @Override public boolean containsKey(Object key) { return nodes.containsKey(key); } /** * 当前实例中是否存在对应的{@link TreeEntry}实例 * * @param value {@link TreeEntry}实例 * @return 是否 */ @Override public boolean containsValue(Object value) { return nodes.containsValue(value); } /** * 获取key对应的节点 * * @param key key * @return 节点 */ @Override public TreeEntry get(Object key) { return nodes.get(key); } /** * 将指定节点从当前{@link Map}中删除 *

    *
  • 若存在父节点或子节点,则将其断开其与父节点或子节点的引用关系;
  • *
  • * 若同时存在父节点或子节点,则会在删除后将让子节点直接成为父节点的子节点,比如:
    * 现有引用关系 a -> b -> c,删除 b 后,将有 a -> c *
  • *
* * @param key 节点的key * @return 删除的且引用关系已经改变的节点,若key没有对应节点,则返回null */ @Override public TreeEntry remove(Object key) { final TreeEntryNode target = nodes.remove(key); if (ObjectUtil.isNull(target)) { return null; } // 若存在父节点: // 1.将该目标从父节点的子节点中移除 // 2.将目标的子节点直接将目标的父节点作为父节点 if (target.hasParent()) { final TreeEntryNode parent = target.getDeclaredParent(); final Map> targetChildren = target.getChildren(); parent.removeDeclaredChild(target.getKey()); target.clear(); targetChildren.forEach((k, c) -> parent.addChild((TreeEntryNode) c)); } return target; } /** * 将当前集合清空,并清除全部节点间的引用关系 */ @Override public void clear() { nodes.values().forEach(TreeEntryNode::clear); nodes.clear(); } /** * 返回当前实例中全部的key组成的{@link Set}集合 * * @return 集合 */ @Override public Set keySet() { return nodes.keySet(); } /** * 返回当前实例中全部{@link TreeEntry}组成的{@link Collection}集合 * * @return 集合 */ @Override public Collection> values() { return new ArrayList<>(nodes.values()); } /** * 由key与{@link TreeEntry}组成的键值对实体的{@link Set}集合。 * 注意,返回集合中{@link Map.Entry#setValue(Object)}不支持调用。 * * @return 集合 */ @Override public Set>> entrySet() { return nodes.entrySet().stream() .map(this::wrap) .collect(Collectors.toSet()); } /** * 将{@link TreeEntryNode}包装为{@link EntryNodeWrapper} */ private Map.Entry> wrap(Map.Entry> nodeEntry) { return new EntryNodeWrapper<>(nodeEntry.getValue()); } // ====================== ForestMap接口实现 ====================== /** * 添加一个节点 *
    *
  • 若key对应节点不存在,则以传入的键值创建一个新的节点;
  • *
  • 若key对应节点存在,则将该节点的值替换为{@code node}指定的值;
  • *
* * @param key 节点的key * @param value 节点的value * @return 节点,若key已有对应节点,则返回具有旧值的节点,否则返回null */ @Override public TreeEntryNode putNode(K key, V value) { TreeEntryNode target = nodes.get(key); if (ObjectUtil.isNotNull(target)) { final V oldVal = target.getValue(); target.setValue(value); return target.copy(oldVal); } target = new TreeEntryNode<>(null, key, value); nodes.put(key, target); return null; } /** * 同时添加父子节点: *
    *
  • 若{@code parentKey}或{@code childKey}对应的节点不存在,则会根据键值创建一个对应的节点;
  • *
  • 若{@code parentKey}或{@code childKey}对应的节点存在,则会更新对应节点的值;
  • *
* 该操作等同于: *
	 *     TreeEntry<K, V>  parent = putNode(parentKey, parentValue);
	 *     TreeEntry<K, V>  child = putNode(childKey, childValue);
	 *     linkNodes(parentKey, childKey);
	 * 
* * @param parentKey 父节点的key * @param parentValue 父节点的value * @param childKey 子节点的key * @param childValue 子节点的值 */ @Override public void putLinkedNodes(K parentKey, V parentValue, K childKey, V childValue) { linkNodes(parentKey, childKey, (parent, child) -> { parent.setValue(parentValue); child.setValue(childValue); }); } /** * 添加子节点,并为子节点指定父节点: *
    *
  • 若{@code parentKey}或{@code childKey}对应的节点不存在,则会根据键值创建一个对应的节点;
  • *
  • 若{@code parentKey}或{@code childKey}对应的节点存在,则会更新对应节点的值;
  • *
* * @param parentKey 父节点的key * @param childKey 子节点的key * @param childValue 子节点的值 */ @Override public void putLinkedNodes(K parentKey, K childKey, V childValue) { linkNodes(parentKey, childKey, (parent, child) -> child.setValue(childValue)); } /** * 为指定的节点建立父子关系,若{@code parentKey}或{@code childKey}对应节点不存在,则会创建一个对应的值为null的空节点 * * @param parentKey 父节点的key * @param childKey 子节点的key * @param consumer 对父节点和子节点的操作,允许为null */ @Override public void linkNodes(K parentKey, K childKey, BiConsumer, TreeEntry> consumer) { consumer = ObjectUtil.defaultIfNull(consumer, (parent, child) -> { }); final TreeEntryNode parentNode = nodes.computeIfAbsent(parentKey, t -> new TreeEntryNode<>(null, t)); TreeEntryNode childNode = nodes.get(childKey); // 1.子节点不存在 if (ObjectUtil.isNull(childNode)) { childNode = new TreeEntryNode<>(parentNode, childKey); consumer.accept(parentNode, childNode); nodes.put(childKey, childNode); return; } // 2.子节点存在,且已经是该父节点的子节点了 if (ObjectUtil.equals(parentNode, childNode.getDeclaredParent())) { consumer.accept(parentNode, childNode); return; } // 3.子节点存在,但是未与其他节点构成父子关系 if (false == childNode.hasParent()) { parentNode.addChild(childNode); } // 4.子节点存在,且已经与其他节点构成父子关系,但是允许子节点直接修改其父节点 else if (allowOverrideParent) { childNode.getDeclaredParent().removeDeclaredChild(childNode.getKey()); parentNode.addChild(childNode); } // 5.子节点存在,且已经与其他节点构成父子关系,但是不允许子节点直接修改其父节点 else { throw new IllegalArgumentException(StrUtil.format( "[{}] has been used as child of [{}], can not be overwrite as child of [{}]", childNode.getKey(), childNode.getDeclaredParent().getKey(), parentKey )); } consumer.accept(parentNode, childNode); } /** * 移除指定父节点与其直接关联的子节点间的引用关系,但是不会将该节点从集合中删除 * * @param parentKey 父节点的key * @param childKey 子节点 */ @Override public void unlinkNode(K parentKey, K childKey) { final TreeEntryNode childNode = nodes.get(childKey); if (ObjectUtil.isNull(childNode)) { return; } if (childNode.hasParent()) { childNode.getDeclaredParent().removeDeclaredChild(childNode.getKey()); } } /** * 树节点 * * @param key类型 * @author huangchengxing */ public static class TreeEntryNode implements TreeEntry { /** * 根节点 */ private TreeEntryNode root; /** * 父节点 */ private TreeEntryNode parent; /** * 权重,表示到根节点的距离 */ private int weight; /** * 子节点 */ private final Map> children; /** * key */ private final K key; /** * 值 */ private V value; /** * 创建一个节点 * * @param parent 节点的父节点 * @param key 节点的key */ public TreeEntryNode(TreeEntryNode parent, K key) { this(parent, key, null); } /** * 创建一个节点 * * @param parent 节点的父节点 * @param key 节点的key * @param value 节点的value */ public TreeEntryNode(TreeEntryNode parent, K key, V value) { this.parent = parent; this.key = key; this.value = value; this.children = new LinkedHashMap<>(); if (ObjectUtil.isNull(parent)) { this.root = this; this.weight = 0; } else { parent.addChild(this); this.weight = parent.weight + 1; this.root = parent.root; } } /** * 获取当前节点的key * * @return 节点的key */ @Override public K getKey() { return key; } /** * 获取当前节点与根节点的距离 * * @return 当前节点与根节点的距离 */ @Override public int getWeight() { return weight; } /** * 获取节点的value * * @return 节点的value */ @Override public V getValue() { return value; } /** * 设置节点的value * * @param value 节点的value * @return 节点的旧value */ @Override public V setValue(V value) { final V oldVal = getValue(); this.value = value; return oldVal; } // ================== 父节点的操作 ================== /** * 从当前节点开始,向上递归当前节点的父节点 * * @param includeCurrent 是否处理当前节点 * @param consumer 对节点的操作 * @param breakTraverse 是否终止遍历 * @return 遍历到的最后一个节点 */ TreeEntryNode traverseParentNodes( boolean includeCurrent, Consumer> consumer, Predicate> breakTraverse) { breakTraverse = ObjectUtil.defaultIfNull(breakTraverse, a -> n -> false); TreeEntryNode curr = includeCurrent ? this : this.parent; while (ObjectUtil.isNotNull(curr)) { consumer.accept(curr); if (breakTraverse.test(curr)) { break; } curr = curr.parent; } return curr; } /** * 当前节点是否为根节点 * * @return 当前节点是否为根节点 */ public boolean isRoot() { return getRoot() == this; } /** * 获取以当前节点作为叶子节点的树结构,然后获取该树结构的根节点 * * @return 根节点 */ @Override public TreeEntryNode getRoot() { if (ObjectUtil.isNotNull(this.root)) { return this.root; } else { this.root = traverseParentNodes(true, p -> { }, p -> !p.hasParent()); } return this.root; } /** * 获取当前节点直接关联的父节点 * * @return 父节点,当节点不存在对应父节点时返回null */ @Override public TreeEntryNode getDeclaredParent() { return parent; } /** * 获取以当前节点作为叶子节点的树结构,然后获取该树结构中当前节点的指定父节点 * * @param key 指定父节点的key * @return 指定父节点,当不存在时返回null */ @Override public TreeEntryNode getParent(K key) { return traverseParentNodes(false, p -> { }, p -> p.equalsKey(key)); } /** * 获取以当前节点作为根节点的树结构,然后遍历所有节点 * * @param includeSelf 是否处理当前节点 * @param nodeConsumer 对节点的处理 */ @Override public void forEachChild(boolean includeSelf, Consumer> nodeConsumer) { traverseChildNodes(includeSelf, (index, child) -> nodeConsumer.accept(child), null); } /** * 指定key与当前节点的key是否相等 * * @param key 要比较的key * @return 是否key一致 */ public boolean equalsKey(K key) { return ObjectUtil.equal(getKey(), key); } // ================== 子节点的操作 ================== /** * 从当前节点开始,按广度优先向下遍历当前节点的所有子节点 * * @param includeCurrent 是否包含当前节点 * @param consumer 对节点与节点和当前节点的距离的操作,当{@code includeCurrent}为false时下标从1开始,否则从0开始 * @param breakTraverse 是否终止遍历,为null时默认总是返回{@code true} * @return 遍历到的最后一个节点 */ TreeEntryNode traverseChildNodes( boolean includeCurrent, BiConsumer> consumer, BiPredicate> breakTraverse) { breakTraverse = ObjectUtil.defaultIfNull(breakTraverse, (i, n) -> false); final Deque>> keyNodeDeque = CollUtil.newLinkedList(CollUtil.newArrayList(this)); boolean needProcess = includeCurrent; int index = includeCurrent ? 0 : 1; TreeEntryNode lastNode = null; while (!keyNodeDeque.isEmpty()) { final List> curr = keyNodeDeque.removeFirst(); final List> next = new ArrayList<>(); for (final TreeEntryNode node : curr) { if (needProcess) { consumer.accept(index, node); if (breakTraverse.test(index, node)) { return node; } } else { needProcess = true; } CollUtil.addAll(next, node.children.values()); } if (!next.isEmpty()) { keyNodeDeque.addLast(next); } lastNode = CollUtil.getLast(next); index++; } return lastNode; } /** * 添加子节点 * * @param child 子节点 * @throws IllegalArgumentException 当要添加的子节点已经是其自身父节点时抛出 */ void addChild(TreeEntryNode child) { if (containsChild(child.key)) { return; } // 检查循环引用 traverseParentNodes(true, s -> Assert.notEquals( s.key, child.key, "circular reference between [{}] and [{}]!", s.key, this.key ), null); // 调整该节点的信息 child.parent = this; child.traverseChildNodes(true, (i, c) -> { c.root = getRoot(); c.weight = i + getWeight() + 1; }, null); // 将该节点添加为当前节点的子节点 children.put(child.key, child); } /** * 移除子节点 * * @param key 子节点 */ void removeDeclaredChild(K key) { final TreeEntryNode child = children.get(key); if (ObjectUtil.isNull(child)) { return; } // 断开该节点与其父节点的关系 this.children.remove(key); // 重置子节点及其下属节点的相关属性 child.parent = null; child.traverseChildNodes(true, (i, c) -> { c.root = child; c.weight = i; }, null); } /** * 获取以当前节点作为根节点的树结构,然后获取该树结构中的当前节点的指定子节点 * * @param key 指定子节点的key * @return 节点 */ @Override public TreeEntryNode getChild(K key) { return traverseChildNodes(false, (i, c) -> { }, (i, c) -> c.equalsKey(key)); } /** * 获取当前节点直接关联的子节点 * * @return 节点 */ @Override public Map> getDeclaredChildren() { return new LinkedHashMap<>(this.children); } /** * 获取以当前节点作为根节点的树结构,然后按广度优先获取该树结构中的当前节点的全部子节点 * * @return 节点 */ @Override public Map> getChildren() { final Map> childrenMap = new LinkedHashMap<>(); traverseChildNodes(false, (i, c) -> childrenMap.put(c.getKey(), c), null); return childrenMap; } /** * 移除对子节点、父节点与根节点的全部引用 */ void clear() { this.root = null; this.children.clear(); this.parent = null; } /** * 比较目标对象与当前{@link TreeEntry}是否相等。
* 默认只要{@link TreeEntry#getKey()}的返回值相同,即认为两者相等 * * @param o 目标对象 * @return 是否 */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || this.getClass().equals(o.getClass()) || ClassUtil.isAssignable(this.getClass(), o.getClass())) { return false; } final TreeEntry treeEntry = (TreeEntry) o; return ObjectUtil.equals(this.getKey(), treeEntry.getKey()); } /** * 返回当前{@link TreeEntry}的哈希值。
* 默认总是返回{@link TreeEntry#getKey()}的哈希值 * * @return 哈希值 */ @Override public int hashCode() { return Objects.hash(getKey()); } /** * 复制一个当前节点 * * @param value 复制的节点的值 * @return 节点 */ TreeEntryNode copy(V value) { TreeEntryNode copiedNode = new TreeEntryNode<>(this.parent, this.key, ObjectUtil.defaultIfNull(value, this.value)); copiedNode.children.putAll(children); return copiedNode; } } /** * {@link java.util.Map.Entry}包装类 * * @param key类型 * @param value类型 * @param 包装的{@link TreeEntry}类型 * @see #entrySet() * @see #values() */ public static class EntryNodeWrapper> implements Map.Entry> { private final N entryNode; EntryNodeWrapper(N entryNode) { this.entryNode = entryNode; } @Override public K getKey() { return entryNode.getKey(); } @Override public TreeEntry getValue() { return entryNode; } @Override public TreeEntry setValue(TreeEntry value) { throw new UnsupportedOperationException(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy