Java 8 中的 HashMap

Java 8系列之重新认识HashMap HashMap的内部功能实现很多,本文主要从根据key获取哈希桶数组索引位置、put方法的详细执行、扩容过程三个具有代表性的点深入展开讲解。# 1. 确定哈希桶数组索引位置不管增加、删除、查找键值对,定位到哈希桶数组的位置都是很关键的第一步。 阅读详情
红黑树(red black tree)
特点
  • 一个节点是红色或黑色
  • 根节点是黑色
  • 如果一个节点是红色,那么它的子节点必须是黑色
  • 一个节点到一个null引用的每一条路径必须包含相同数目的黑色节点(红色节点不影响)
两种旋转方式和一种颜色变换
  • 单旋转方式
  • 双旋转方式(需要两次反方向的单旋转)
  • 当两个子节点均为红色的时候,执行颜色变换,因为插入的是红色节点,会产生冲突。例如根节点的子节点是红色,两个叶子节点变成黑色,根节点变成红色,再变成黑色。
问题
  1. 为什么插入的总是红色节点?

    因为插入前,树都是构建好的,如果插入的是黑色节点,就破坏了每一条路径必须包含相同数目的黑色节点这一特性
  2. 为什么进行旋转?

    因为插入时总是红色节点,如果不旋转的话,违背了一个节点是红色,那么它的子节点必须是黑色这一特性
  3. 为什么进行颜色变换?

    图示的第一种旋转,红色节点 X 的插入破坏了红黑树结构,所以进行旋转,旋转后的结构如图所示,旋转后,P 和 G 点如果维持本来的颜色,就会违背第三、四条特性,所以,进行颜色变换。
  4. 与 AVL(Adelson-Velsky and Landis Tree) 树(平衡二叉查找树)比较?

    红黑树不是通过递归方式,而是通过循环的方式来实现,不需要保存节点高度字段,节省内存
    两者最坏操作时间复杂度都是O(logN)
HashMap特性
  1. 允许 key 、 value 任一值或同时为 null
  2. HashMap 的元素时无序,不稳定
  3. HashMap 写操作线程不安全,读操作线程安全
  4. 支持已任何形式创建的迭代器
  5. 不支持除迭代本身方法(remove())改变集合元素,否则会抛出 ConcurrentModificationException 异常
HashMap与HashTable区别
  1. HashTable 读写操作是线程安全
  2. HashTable 不允许 key 、 value 任一值或同时为 null
HashMap内部类
内部类简介
  1. Node (static class):基于 hash 的链表节点,由 LinkedHashMap.Entry 、 TreeNode 继承实现
    • TreeNode (static final class)
  2. Values (final Class):HashMap中的 value 集合
  3. KeySet (final Class):HashMap中的 key 集合
  4. EntrySet (final Class):HashMap中的 Entry 集合
  5. HashIterator (abstract class):抽象迭代器
    • KeyIterator (final Class): HashMap中的 Key 集合的迭代器
    • ValueIterator (final class):HashMap中的 Value 集合的迭代器
    • EntryIterator (final class):HashMap中的 Entry 集合的迭代器
  6. HashMapSpliterator (static class):抽象的并行迭代器
    • KeySpliterator (static final class):HashMap中的 Key 集合的并行迭代器
    • ValueSpliterator (static final class):HashMap中的 Value 集合的并行迭代器
    • EntrySpliterator (static final class):HashMap中的 Entry 集合的并行迭代器
Node 内部类
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;

            return o instanceof Map.Entry<?, ?> e
                    && Objects.equals(key, e.getKey())
                    && Objects.equals(value, e.getValue());
        }
    }
TreeNode 内部类
static final class TreeNode<K, V> extends LinkedHashMap.Entry<K, V>
    {
        TreeNode<K, V> parent; // red-black tree links
        TreeNode<K, V> left;
        TreeNode<K, V> right;
        TreeNode<K, V> prev; // needed to unlink next upon deletion
        boolean red;

        TreeNode(int hash, K key, V val, Node<K, V> next)
        {
            super(hash, key, val, next);
        }
        
        final void treeify(Node<K,V>[] tab)
        {
            // ......
        }
        
        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x)
        {
            // ......
        }
        
        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<K,V> p)
        {
            // ......
        }
        
        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p)
        {
            // ......
        }
        
        // ......其余方法省略
    }

HashMap中内部变量
   /**
     * 默认桶的数量 为 16 必须是 2 的幂次方
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 桶的最大数量为 2 的 30 幂次方
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 桶的负载因子,当存有数据的桶的数量超过 75% ,就会扩容为 2 倍桶数量,并自动进行再散列(rehashed)
     * 可以查看构造函数
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 桶内数据量超过这个阈值就会将桶内数据结构从链表转为红黑树
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 桶内数据量小于这个阈值就会将桶内数据结构从红黑树退化为链表
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * 如果桶的数量小于 64 ,会先扩容,直至桶的数量超过 64 
     * 这样是为了减少形成很满的桶,因为桶的数量越多,越不容易造成桶满
     * 借鉴了 <see>https://stackoverflow.com/questions/43911369/hashmap-java-8-implementation</see>
     */
    static final int MIN_TREEIFY_CAPACITY = 64;
HashMap插入
put()方法

使用内部 putval()方法

putval()方法
  • putval方法参数说明
* @param hash hash for key (key 的 Hash 值)
* @param key the key  
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value (如果键存在,是否需要更新 value值,true 更新 false 不更新)
* @param evict if false, the table is in creation mode.(如果为false,表示该表处于创建模式,jdk1.8版本里面使用该参数的方法是空实现,故暂没有作用)
* @return previous value, or null if none(返回之前的值,如果没有直接返回 null)
  • 代码逻辑执行过程
  1. 表是否为null或表长度是否为零,决定了是否先进行初始化。

    • 1.1 初始化表,调用方法resize()
    • 1.2 Hash计算后,如果对应桶内为null,往桶内添加数据,此时桶内使用数据结构为链表
    • 1.3 Hash计算后,如果对应桶内不为null,判断key的 Hash 值以及 key 值是否相等,
      • 1.3.1 相等,直接覆盖
      • 1.3.2 不相等,判断节点结构是否为红黑树结构(TreeNode)实例,是则直接使用 putTreeVal() 方法
      • 1.3.3 判断节点结构是否为红黑树节点结构(TreeNode)实例,不是则直接将数据插入链表末端,并判断链表长度是否大于等于 8 ,是则开始进行将链表进化为红黑树的操作(treeifyBin)
  2. treeifyBin()

    • 2.1 如果表为null或者表长度小于 64 ,则进行表长度扩容
    • 2.2 如果表长度大于 64 且表最后一个桶内数据不为null,则将桶内链表的节点结构转换为TreeNode,并连成一个链表
    • 2.3 进化红黑树(treeify)
  3. 转换红黑树代码解析

  • treeify()
final void treeify(Node<K,V>[] tab) {
            TreeNode<K,V> root = null;
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
                next = (TreeNode<K,V>)x.next;
                x.left = x.right = null;
                if (root == null) {
                  // 配置红黑树根节点
                    x.parent = null;
                    x.red = false;
                    root = x;
                }
                else {
                  // 存在根节点后,根据外层的for循环,知晓当前节点 x 的信息
                  // 开始从上往下进行红黑树节点添加,以下的 for 循环是核心循环,进行节点添加
                    K k = x.key;
                    int h = x.hash;
                    Class<?> kc = null;
                    // for 循环是为了寻找一个空的节点位置,将元素放入
                    for (TreeNode<K,V> p = root;;) {
                      // for 循环中没有控制条件,需循环内部跳出
                        int dir, ph;
                        K pk = p.key;
                        if ((ph = p.hash) > h)
                          // 节点的 Hash 值 大于 当前元素 Hash 值,放左节点
                            dir = -1;
                        else if (ph < h)
                          // 节点的 Hash 值 小于 当前元素 Hash 值,放右节点
                            dir = 1;
                        else if ((kc == null &&
                                  (kc = comparableClassFor(k)) == null) ||
                                 (dir = compareComparables(kc, k, pk)) == 0)
                          // 先尝试通过Comparable比较两个对象(当前pk的key对象和x的key对象)
                          // 1. 先通过 comparableClassFor 方法判断两者是否可以进行 Comparable
                          // 2. 如果可以,通过 compareComparables 方法进行比较
                          // 判断条件中的方法没有分出胜负则使用此方法分出胜负
                            dir = tieBreakOrder(k, pk);
                        TreeNode<K,V> xp = p;
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
                            x.parent = xp;
                            if (dir <= 0)
                                xp.left = x;
                            else
                                xp.right = x;
                            // 维护红黑树添加节点后的结构
                            root = balanceInsertion(root, x);
                            break;
                        }
                    }
                }
            }
          //Ensures that the given root is the first node of its bin
          // 确保根节点在链表的第一个
            moveRootToFront(tab, root);
        }
  • 需要被插入的元素的key对象是否实现了 Comparable 接口
static Class<?> comparableClassFor(Object x) {
      // 当前元素key 是否实现了 Comparable 接口
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
              // 如果 x 是字符串对象,直接返回,因为 String 实现了Comparable接口
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
              // 获取 x 实现了哪些接口,包含泛型接口(泛型信息)
                for (Type t : ts) {
                    if ((t instanceof ParameterizedType) &&
                        ((p = (ParameterizedType) t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                  // 如果当前接口t是个泛型接口
                  // 如果该泛型接口t的原始类型p 是 Comparable 接口
                  // 如果该Comparable接口p只定义了一个泛型参数
                  // 如果这一个泛型参数的类型就是c,那么返回c
                        return c;
                }
            }
        }
        return null;
    }
  • 通过 Comparable 比较大小,确定节点位置
static int compareComparables(Class<?> kc, Object k, Object x) {
      // 如果 x == null 或者 x 的实现类不是 comparableClassFor 获取的类,直接返回 0
      // 如果 x != null 或者 x 的实现类是 comparableClassFor 获取的类
      // 通过 Comparable 比较大小,返回比较结果
        return (x == null || x.getClass() != kc ? 0 : ((Comparable)k).compareTo(x));
    }
  • 插入节点后,平衡红黑树结构
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {
          // 印证了之前说的新加入节点都是红色的
          x.red = true;
          // xp x 的父节点
          // xpp x 的祖父节点
          // xppl x 的祖父的左节点
          // xppr x 的祖父的右节点
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
              if ((xp = x.parent) == null) {
                // 如果 x 的父节点是 null 说明 x 节点为 根节点
                // 红黑树特性 根节点必须黑色
                    x.red = false;
                    return x;
                }
                else if (!xp.red || (xpp = xp.parent) == null)
                  // 进入 else 说明 x 不是根节点,
                  // x 的父节点为黑色,可以在下面直接添加红色节点,直接返回根,
                  // x 的祖父节点为空,表示 x 的父节点是根节点,且调用该方法前,节点已经添加完成,所以直接返回根节点
                  return root;
              // 进入下面的执行逻辑表示
              // 1. x 的父节点xp是红色的,
              // 2. x 的祖父节点xpp不为空
              // 这样就遇到两个红色节点相连的问题,所以必须经过颜色变换和双旋转
                if (xp == (xppl = xpp.left)) {
                  // x 的父节点是 x 祖父节点的左节点
                    if ((xppr = xpp.right) != null && xppr.red) {
                      // x 的祖父节点的右节点不是 null 且右节点是红色,则证明祖父节点的左节点也是红色
                      // 因为红色节点下面不能再挂红色节点,所以需要将祖父节点的左右节点全部变成黑色,祖父节点变成红色(就是前面说的颜色变换)
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                      // 为什么让 x = xpp ?因为 xpp 变成红色节点后可能与 xpp 的父节点发生冲突(两个红色节点相连),
                      // 就此形成了图示的第二种旋转,所以需要从下往上旋转
                        x = xpp;
                    }
                    else {
                      // x 的祖父节点的右节点是 null 且右节点是黑色
                      // 那么此时的结构位置就有两种情况
                      // 1. xpp->xp(xxp的左节点)->x(xp的右节点)
                        if (x == xp.right) {
                          // 向左旋(图示的单旋转方式)注意进行左旋的节点是 x 的父节点 
                            root = rotateLeft(root, x = xp);
                          // 将xp 的父节点执向 x 的祖父节点
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        // 2. xpp->xp(xxp的左节点)->x(xp的左节点) 或者上面旋转后,也会形成这种结构
                        if (xp != null) {
                          // x 的父节点变为黑色
                            xp.red = false;
                            if (xpp != null) {
                              // x 的祖父节点变为红色
                                xpp.red = true;
                                // 向右旋 (图示的单旋转方式)
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else {
                  // x 的父节点是祖父节点的右节点
                    if (xppl != null && xppl.red) {
                      // x 的祖父节点的左节点不是 null 且是红色节点
                      // 那么 x 的父节点 肯定也是红色节点,这样就符合图示的第三种旋转
                      // x 的祖父节点的左节点变成黑色节点,x 的父节点也变成黑色节点 ,x 的祖父节点变成红色节点
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        // 为什么 x = xpp ? 因为 x 的祖父节点变成红色节点后,可能产生两个红色节点相连的冲突,即图示的第二种旋转
                      // 所以将x = xpp ,进行从下而上的旋转
                        x = xpp;
                    }
                    else {
                      // x 的祖父节点的左节点是 null 且是黑色节点,那么 x 的父节点肯定是黑色
                      // 此时结构肯定有两种
                      // 1. xpp右->xp左->x
                        if (x == xp.left) {
                          // 向右旋
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        // 2. xpp右->xp右->x 或上述旋转后形成的该结构
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                // 向左旋
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }
  • rotateLeft
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {
          // root 表示根节点
          // p 表示 x 的父节点 xp
          // pp 表示 父节点的父节点 祖父节点
          // rl 表示 右节点的左节点
          // r 表示 右节点
          // 左旋就是将某个节点旋转为其右节点的左节点
          TreeNode<K,V> r, pp, rl;
            if (p != null && (r = p.right) != null) {
              // 如果符合的话,p 的右节点指向 rl 否则指向 null
                if ((rl = p.right = r.left) != null)
                    rl.parent = p;
                // 如果pp 为空,实际剩下三个节点
                if ((pp = r.parent = p.parent) == null)
                    (root = r).red = false;
                // 如果pp 不为为空
                else if (pp.left == p)
                    pp.left = r;
                else
                    pp.right = r;
                r.left = p;
                p.parent = r;
            }
            return root;
        }
  • 图示左旋过程
// 执行完该代码,形成图示结构
if (p != null && (r = p.right) != null) {
    if ((rl = p.right = r.left) != null)
       rl.parent = p;
```java // (图示 PP != null) 执行完该代码,形成图示结构 if ((pp = r.parent = p.parent) == null) (root = r).red = false; ``` ```java // (图示 PP != null) 执行完该代码,形成图示结构(图示 pp.left == p 成立) else if (pp.left == p) pp.left = r; else pp.right = r; r.left = p; p.parent = r; ``` ```java // (图示 PP == null) 执行完该代码,形成图示结构 if ((pp = r.parent = p.parent) == null) (root = r).red = false; ``` ```java // (图示 PP == null) 执行完该代码,形成图示结构 r.left = p; p.parent = r; ```
  • rotateRight
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                               TreeNode<K,V> p) {
          // l 左节点
          // pp 祖父节点
          // lr 左节点的右节点
          // 右旋就是将某个节点旋转为其左节点(孩子)的右节点(孩子)
          TreeNode<K,V> l, pp, lr;
            if (p != null && (l = p.left) != null) {
                if ((lr = p.left = l.right) != null)
                    lr.parent = p;
                if ((pp = l.parent = p.parent) == null)
                    (root = l).red = false;
                else if (pp.right == p)
                    pp.right = l;
                else
                    pp.left = l;
                l.right = p;
                p.parent = l;
            }
            return root;
        }
// 执行完图示代码,形成右旋开始前的红黑树结构
// x 的父节点变为黑色
xp.red = false;
if (xpp != null) {
    // x 的祖父节点变为红色
    xpp.red = true;
    // 向右旋 (图示的单旋转方式)
}
- 图示右旋过程 ```java // 执行完图示代码,形成图示结构 if (p != null && (l = p.left) != null) { if ((lr = p.left = l.right) != null) lr.parent = p; ``` ```java // ( 图示 pp != null )执行完图示代码,形成图示结构 if ((pp = l.parent = p.parent) == null) (root = l).red = false; ``` ```java // ( 图示 pp != null )执行完图示代码,形成图示结构 else pp.left = l; l.right = p; p.parent = l; ``` ```java // ( 图示 pp == null )执行完图示代码,形成图示结构 if ((pp = l.parent = p.parent) == null) (root = l).red = false; ``` ```java // ( 图示 pp == null )执行完图示代码,形成图示结构 l.right = p; p.parent = l; ```
  • 左旋和右旋结合起来的旋转过程

问题:

  1. 为什么右旋前,需要将 x 节点变成黑色?

    因为这样就可以不考虑 xppp 节点的颜色,即使 xppp 节点颜色是红色,可以进行再次平衡旋转

以上都是拿 x 的父节点是祖父节点的左节点的情况,x 的父节点是祖父节点的右节点的情况,与之相反。

HashMap在Java8中的红黑树  红黑树在HashMap中的应用我们知道在jdk1.8之前,HashMap采用链表的方式解决冲突,不过在更新1.8版本之后,HashMap采用了链表加红黑树的方式来优化了结构。话不多说,我们来看源码:/** * The bin count threshold for using a tree rather than list for a * bin. Bins are con... 阅读详情

相关推荐

Java8 中的HashMap底层原理

版本回顾: HashMap是我们日常开发或者学习中常用的数据结构,了解它的底层原理可以更好的帮助我们使用它。首先我们知道在Java8之前,HashMap采用数组+链表的结构实现,即使用链表处理冲突,同一个hash值的节点都存储在一个链表里。但是当同一个桶中的元素较多时,即hash值相等的元素较多时,通过key依次查找的效率较低。 而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。 首先我们看看他的结构: 左边是一个

千寻的博客 938

Java 8 HashMap 详解

文章目录1. HashMap 存储结构1.1 HashMap底层结构1.2 HashMap 为什么采用数组 + 链表Reference: 解决hash冲突的四种方法:1.2 HashMap 数组结构可否被替代1.3 HashMap 为何不直接使用红黑树1.4 HashMap 红黑树什么时候退化为链表2. HashMap 存取过程2.1 HashMap put()方法2.2 HashMap get(...

谈谈1974 4443

Java8 HashMap源码分析

今天,我们主要来研究一下在Java8中HashMap的数据结构,以及一些重要方法的具体实现。研究HashMap的源代码之前,我们首先来研究一下常用的三种数据结构:数组、链表和红黑数。数组作为一种基本的数据结构,以线性的方式组织数据,按数据的插入顺序来排列数据。在内存中,数组的物理组织形式是一段连续的磁盘空间。在数据操作上,由于数组的物理特点,可以在O(1)的时间复杂度内完成数据的查找。链表在逻辑特...

beacon的专栏 2010

Java8集合之HashMap的hash计算、扩容等问题

周末专门学习了下java8中的HashMap,下面对几个重要知识点做下总结归纳。本文省略了对HashMap的大体介绍,直接解释java8中的难点,因此阅读此文需要对HashMap有基本的理解,下面先重点记忆下HashMap的重要知识点(1)HashMap初始容量16,每次扩容都是2倍,引申得到HashMap的容量必为2的n次幂(2)负载因子为0.75,该值过大,会导致冲突的几率加大,频繁扩容,过小可能导致空间的浪费(3)java8中链表长度大于8,且数组长度大于等于64时会转换为红黑树。

CRUD工程师 4563

AI时代,程序员何去何从?别慌,看完这篇你就明白了

AI时代程序员的发展方向 摘要:随着AI代码助手的普及,程序员行业面临变革。本文通过实际案例展示AI写代码的能力与局限:AI能生成基础功能代码(如RESTful API、数据处理脚本),但缺乏业务理解、系统设计和复杂逻辑处理能力。程序员的核心价值在于需求分析、架构设计、性能优化等高层能力。未来程序员应: 掌握AI工具提升效率 专注于创新和架构设计 培养业务理解能力 持续学习新技术 AI不会取代程序员,但会淘汰不会使用AI的程序员。建议开发者拥抱AI工具,将其作为助手而非威胁,聚焦高价值工作。

yunque_xianguang的博客 908

TL3562/3568内核驱动移植实战:解决无锡沐创N500L-AM4网口异常问题

本文详细介绍了在TL3562/3568平台上移植无锡沐创N500L-AM4网卡驱动的实战经验,重点解决了第四个网口通讯异常问题。通过升级驱动版本至rnpgbe-0.2.3,生成关键头文件并正确配置内核,最终实现四个网口稳定工作。文章还提供了性能优化建议和避坑指南,为类似硬件驱动移植项目提供参考。

weixin_28273593的博客 180

【Java】关于JDK 8的HashMap

HashMap 主要用来存放键值对,它基于哈希表的 Map 接口实现,是常用的 Java 集合之一,是非线程安全的。HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。

DreamSun的博客 792

ns3-gym入门(二):linear-mesh例子详解

学习ns3-gym中关于IEEE 802.11网状网络中控制随机接入的例子linear-mesh

zoe2222226666的博客 1843

Java 8系列之重新认识HashMap8

HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型。随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实现进行了优化,例如引入红黑树的数据结构和扩容的优化等。本文结合JDK1.7和JDK1.8的区别,深入探讨HashMap的结构实现和功能原理。

2401_89465532的博客 1046

如何区分Info与CoWoS封装?

这篇文章主要从Info、CoWoS封装的定义、Interposer的种类以及Info、CoWoS封装的种类三方面进行了介绍,希望读者能够有所收获

wangyubo1的博客 7938

速腾R128雷达+海康全景相机ROS数据采集避坑实录:从‘雷达不转’到MATLAB标定的完整流程

本文详细记录了速腾R128激光雷达与海康威视全景相机在ROS环境下的数据采集与MATLAB标定全流程。从硬件配置的常见陷阱(如雷达旋转参数设置)、驱动编译的依赖管理,到多传感器同步的实战技巧和MATLAB标定方法,提供了完整的避坑指南和技术方案。特别针对RoboSense R128雷达和海康相机的特殊需求给出了具体解决方案。

weixin_28717807的博客 1219

java8 hashmap_Java8 中的 HashMap

扩容是HashMap最常见的操作之一,当HashMap中不断地加入新元素,很快Table无法容纳更多元素时,就需要扩大Table的长度来容纳更多元素java中的数组是不能自动扩容的,只能够通过创建新的更大容量的数组,同时进行数据的移动才可以实现扩容功能JDK1.7中的resize--voidresize(intnewCapacity)在java1.7中,扩容机制非...

weixin_39886205的博客 226

KEPServerEX物联网网关(IoT Gateway)REST客户端代理(非原创)

(中文版)资料来源于【OPC-KEPServerEX学习KEPWare交流】905485143群的【455196383】【零下一度】 一。KEPServerEX物联网网关REST客户端代理演示 在KSE中,首先构建一个带有通道/设备/标签的模拟器系统。 例如,下面是三个随机项目。 直接到IoT网关插件,添加新代理。 从类型列表中选择REST Client。 终结点...

-雲飛兄-博客 5807

java8 hashmap_Java8 HashMap实现原理探究

前言:Java8之后新增挺多新东西,在网上找了些相关资料,关于HashMap在自己被血虐之后痛定思痛决定整理一下相关知识方便自己看。图和有些内容参考的这个文章:http://www.importnew.com/16599.htmlHashMap的存储结构如图:一个桶(bucket)上的节点多于8个则存储结构是红黑树,小于8个是单向链表。1:HashMap的一些属性publicclassHash...

weixin_31792645的博客 282

ABAP Secure Storage 在事务码 SM59 中的应用

系统管理人员在进行维护时,应关注相关日志信息,及时发现可能出现的安全异常,并针对不同安全等级的操作制定相应的备份与恢复策略,以降低潜在风险。该区域在设计上具备高度隔离性,仅允许经过授权的组件访问。保存后,系统会将密码存储在 Secure Storage 中,这种处理方式充分体现了 SAP 标准程序在安全设计上的考量。对于系统管理员而言,这种机制在日常维护与审计中提供了有力的支持,可以通过比对存储数据的哈希值或其他完整性校验手段,及时发现异常变动,保障系统长期稳定运行。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 675

Agent智能体:核心概念与架构——建立系统认知

本文摘要介绍了Agent智能体的核心概念与架构设计。主要内容包括: Agent定义与分类:阐述了Agent作为能够感知环境、决策执行的智能实体,重点分析了现代基于大语言模型(LLM)的Agent架构。 核心模块组成:详细说明了Agent的五大核心模块(感知、规划、记忆、工具和执行)及其交互流程。 UML建模展示:通过类图和序列图,清晰呈现了Agent系统的静态结构和动态执行过程。 项目结构设计:给出了模块化的代码目录结构,为实际开发提供参考框架。

lza_csdn2019的博客 634

Java8—HashMap解析

摘要HashMap的内部表示在Java8中得到很大的改进,比如说在Java7中实现HashMap需要1k行代码,而在Java8中需要2K行代码。在Java8中内部仍然以数组实现,但是以节点(Node)来作为Entry存储信息,并且同样也包括链表。一、重要的概念从结构实现来讲,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下如所示。Node是HashMap的一个内部类,实

淡淡的倔强的博客 1713

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java7/8中的HashMap深挖

根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。Java 7 很明显,还是使用头插法的方法进行操作,这种方法在 Java 8 中就进行了相关的改进,开始使用尾插法的方式,而且操作更加简单,同时防止了死循环的现象。这个方法的主要逻辑就是先判断是否需要扩容,需要的话先扩容,然后再将这个新的数据插入到扩容后的数组的相应位置处的链表的表头。

开源驱动,创造价值;坦诚清晰,快速失败;让复杂可计算。 997

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Java 8 中HashMap的改进

HashMap的快速高效,使其使用非常广泛。其原理是,调用hashCode()和equals()方法,并对hashcode进行一定的哈希运算得到相应value的位置信息,将其分到不同的桶里。桶的数量一般会比所承载的实际键值对多。当通过key进行查找的时候,往往能够在常数时间内找到该value。 但是,当某种针对key的hashcode的哈希运算得到的位置信息重复了之后,就发生了哈希碰撞。这会对H

Ghost In Matrix 1670

java8 HashMap的变化

java8 HashMap的变化树形桶Hash表实现的改变java 7的实现java 8的实现HashMap的一些关键常量总结 在java容器中map是我们经常会用到的一种容器。而map中HashMap是我们最长用的map之一,而且在java面试中考察容器时HashMap的实现也基本是必考之一。并且我们常常会听到考察HashMap时会问到是否是线程安全?为什么HashMap在多线程并发的时候可能造成CPU利用率变成100%?在解析HashMap的这些文章的中,都是基于Java 8之前的的实现来讲解的。那么J

feeling890712的博客 1519

java8hashmap_Java 8中的HashMap性能改进

java8hashmap HashMap<K, V>是每个Java程序中快速,通用且无处不在的数据结构。 首先是一些基础知识。 您可能知道,它使用键的hashCode()和equals()方法在存储桶之间拆分值。 存储桶(箱)的数量应略高于映射中的条目数,以使每个存储桶仅保留很少(最好是一个)值。 当按键查找时,我们很快确定了存储桶(使用hashCode()模数number_of_...

最佳 Java 编程 215

JAVA8之HashMap

JAVA8 之 HashMap类继承层次底层数据结构科普搬运hashcode计算机怎么存数为什么要用补码原码反码补码的关系左移<< 右移>>无符号右移>>>(没有<<<这种操作)HashMap中的索引计算默认初始容量什么时机触发扩容扩容什么时机普通链表膨胀为红黑树什么时机红黑树退化为普通链表主要操作时间复杂度安全性 类继承层次 底层数据结构 hash表 +(普通链表 | 红黑树) 科普搬运 hashcode 散列函数,给每个对象一个编号,计算速度

piccasi的专栏 416

Java8 HashMap详解

Java8 HashMapJava8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链

好好学java 1094

Java8 HashMap

HashMap Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其散列表是由数组+链表+红黑树组成。 Java7 HashMap查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)O(n)O(n)。 为了降低这部分的开销,在 Java8 中,当链表中的元素...

eve_z的博客 131

一篇文章搞清楚java7/java8的HashMap、ConcurrentHashMap

本文篇幅较长建议阅读顺序 1、Java7 HashMap 2、Java8 HashMap 3、Java7 ConcurrentHashMap 4、Java8 ConcurrentHashMap 那既然需要切换jdk,那大家根据自己的情况选择阅读,互相之间我会尽量写的独立,不影响阅读,废话不多说,开始吧! 一、Java7 HashMap java 7的HashMap相较于另外我要讲的三个应该是最简单...

paul、的博客 394
上一篇: win10 Jdk 版本切换
下一篇: Idea 插件 FindBugs 安装及使用
香草味小布丁
博客等级 码龄10年 461粉丝 72原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值