jdk源码解读系列一 之 HashMap

手写HashMap源码 (jdk 1.8版本)

– 考虑以后手写一下ConcurrentHashMap源码

知识点

  1. 自己构建出hash冲突现场
  2. 实现散列数据数据结构
  3. 理解hash、get、put、resize、remove 等核心方法的设计原理
  4. 链表转红黑树的时机和过程
  5. 红黑树退化为链表的时机和过程

如何产生hash冲突呢?

  10000000000000000 65536
   0000000000000001 高16位

 110000000000000010 196610
   0000000000000011 高16位

1110000000000000110 458758
   0000000000000111 高16位

11110000000000001110 983054
    0000000000001111 高16位

111110000000000011110 2031646
     0000000000011111 高16位

1111110000000000111110 4128830
      0000000000111111 高16位

11111110000000001111110 8323198
       0000000001111111 高16位

111111110000000011111110 16711934
        0000000011111111 高16位

1111111110000000111111110 33489406
         0000000111111111 高16位

hashMap实现源码,基于阅读然后实现

package top.takefly.map;

import top.takefly.tree.printer.BinaryTreeInfo;
import top.takefly.tree.printer.BinaryTrees;

import java.io.Serializable;
import java.util.function.BiConsumer;

/**
 * @author 戴灵飞 dailingfei
 * @version 1.0
 * @date 2020/2/1 13:29
 * 模仿hashMap
 */
public class HashMap<K, V> implements Map<K, V>, Serializable, Cloneable {

    private static final boolean RED = false;
    private static final boolean BLACK = true;

    //默认容量
    private static final int DEFAULT_CAPACITY = 1 << 4;//16

    private transient int size;//容量

    private transient int modCount;//修改次数

    private static final int TREEIfy_THRESHOLD = 8;

    private static final int MIN_TREEIfy_CAPACITY = 1 << 6;

    private static final int MAX_CAPACITY = 1 << 30;

    private int threshold;

    private float loadFactory = 0.75f;

    private transient Node<K, V>[] table;//哈希表 也称散列表

    /**
     * @return
     */
    private Node<K, V>[] resize() {
        Node<K, V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAX_CAPACITY) {//不允许继续扩容
                newThr = Integer.MAX_VALUE;//将扩容限制设置到最高
                return oldTab;
            } else if ((newCap = (oldCap << 1)) <= MAX_CAPACITY
                    && oldCap >= DEFAULT_CAPACITY) {//允许扩容一倍
                newThr = oldThr << 1;
            }
        } else if (oldThr > 0) {
            newCap = oldThr;
        } else { //oldThr <= 0 && oldCap <= 0
            newCap = DEFAULT_CAPACITY;//第一次初始化为默认容量
            newThr = (int) (DEFAULT_CAPACITY * loadFactory);
        }
        if (newThr == 0) {
            float ft = (float) (newCap * loadFactory);
            newThr = (newCap < MAX_CAPACITY && ft < MAX_CAPACITY) ? (int) ft : Integer.MAX_VALUE;
        }
        Node<K, V>[] newTable = new Node[newCap];//创建新的hash表(散列表)
        table = newTable;//table新的hash表
        threshold = newThr;
        if (oldTab != null) {
            System.out.println("---------resize   开始----------------");
            System.out.println("newCap:" + newCap + ",oldCap:" + oldCap);
            System.out.println();
            for (int i = 0; i < oldCap; i++) {//遍历整个old散列表  i 表示索引
                Node<K, V> e;
                if ((e = oldTab[i]) != null) {
                    oldTab[i] = null;//let gc work
                    if (e.next == null)
                        newTable[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode) {
                        ((TreeNode<K, V>) e).split(this, newCap, i, oldCap);
                    } else {//单链表处理
                        Node<K, V> loHead = null, loTail = null;//低列链表
                        Node<K, V> hiHead = null, hiTail = null, next = null;//低列链表
                        do {
                            //因为capacity是2的幂 所以log(capacity)为多少就表示对应的二进制有多少0
                            //之所以拿元素的hash与oldCap按位与运算,就是判断下一位是否为1
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {//下一位为0
                                if (loHead == null) {
                                    loHead = e;
                                } else {
                                    loTail.next = e;
                                }
                                loTail = e;
                            } else {
                                if (hiHead == null) {
                                    hiHead = e;
                                } else {
                                    hiTail.next = e;
                                }
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loHead != null) {//所在索引不变
                            loTail.next = null;
                            newTable[i] = loHead;
                        }
                        if (hiHead != null) {//上升到oldCap+i
                            hiTail.next = null;
                            newTable[i + oldCap] = hiHead;
                        }
                        if(newCap == 32769){
                        System.out.print("loHead");
                        while (loHead != null) {
                            System.out.print("->" + loHead + ":" + ((newCap - 1) & loHead.hash));
                            loHead = loHead.next;
                        }
                        System.out.println();

                        System.out.print("hiHead");
                        while (hiHead != null) {
                            System.out.print("->" + hiHead + ":" + ((newCap - 1) & hiHead.hash));
                            hiHead = hiHead.next;
                        }
                        System.out.println();
                        }
                    }
                }
            }
            System.out.println("---------resize  结束----------------");
            System.out.println();
        }
        return newTable;
    }

    private static class Node<K, V> {
        int hash;
        K key;
        V value;
        protected Node<K, V> next;

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

        @Override
        public String toString() {
            return key.toString() + "_" + hash;
        }
    }

    private static class TreeNode<K, V> extends Node<K, V> {
        boolean red = true;
        public TreeNode<K, V> pre;
        public TreeNode<K, V> parent;
        public TreeNode<K, V> left;
        public TreeNode<K, V> right;

        TreeNode(int hash, K k, V v, Node<K, V> next) {
            super(hash, k, v, next);
        }

        TreeNode(int hash, K k, V v, TreeNode<K, V> parent) {
            super(hash, k, v, null);
            this.parent = parent;
        }

        @Override
        public String toString() {
            String color = red ? "Red" : "Black";
            K p = parent == null ? null : parent.key;
            K l = left == null ? null : left.key;
            K r = right == null ? null : right.key;
            K n = next == null ? null : next.key;
//            return color + "_k(" + key + ")_p(" + p + ")_l(" + l + ")_r(" + r + ")";
//            return color + "_k(" + key + ")_n(" + n + ")";
            return key + "_";
        }

        public Node<K, V> putVal(int hash, K k, V value) {
            return null;
        }

        /**
         * 将双向链表转化为红黑树
         */
        public 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;
                if (root == null) {//添加到根节点
                    x.parent = null;
                    x.red = false;//设置为黑色
                    root = x;//根节点
                } else {//非跟节点添加
                    K k = x.key;
                    int h = x.hash;
                    int cmp = 0;
                    Class<?> kc;
                    for (TreeNode<K, V> p = root; ; ) {//遍历
                        int ph = p.hash;
                        K pk = p.key;
                        if (ph < h) {
                            cmp = 1;
                        } else if (ph > h) {
                            cmp = -1;
                        } else if (k == null
                                && (
                                (kc = comparableClassFor(k)) == null || (cmp = compareComparables(kc, k, pk)) == 0)) {
                            cmp = tieBreakOrder(k, pk);
                        }
                        TreeNode<K, V> xp = p;//保存父节点
                        if ((p = (cmp <= 0) ? p.left : p.right) == null) {
                            x.parent = xp;//x 表示要插入的节点
                            if (cmp <= 0) {
                                xp.left = x;
                            } else {
                                xp.right = x;
                            }
                            root = balanceInsertion(root, x);//插入节点后平衡
                            break;
                        }
                    }
                }
            }
            moveRootToFront(tab, root);//将root节点移动到对应索引的第一个
        }

        /**
         * 将root节点移到桶的前面
         *
         * @param tab
         * @param root
         */
        public void moveRootToFront(Node<K, V>[] tab, TreeNode<K, V> root) {
            int n, index;// n:用于记录hash表大小  index:用于记录root的索引
            if (tab != null && (n = tab.length) != 0) {
                TreeNode<K, V> first = (TreeNode<K, V>) tab[index = (n - 1) & root.hash];
                if (first != root) {//说明root不是根节点
                    Node<K, V> rn;
                    tab[index] = root;//指定root为根节点
                    TreeNode<K, V> rp = root.pre;
                    if ((rn = root.next) != null) {
                        ((TreeNode<K, V>) rn).pre = pre;
                    }
                    if (rp != null) {
                        rp.next = rn;
                    }
                    if (first != null) {
                        first.pre = root;
                    }
                    root.next = first;
                    root.pre = null;
                    TreeNode<K, V> finalRoot = root;
                    BinaryTrees.println(new BinaryTreeInfo() {
                        @Override
                        public Object root() {
                            return finalRoot;
                        }

                        @Override
                        public Object left(Object node) {
                            return ((TreeNode) node).left;
                        }

                        @Override
                        public Object right(Object node) {
                            return ((TreeNode) node).right;
                        }

                        @Override
                        public Object string(Object node) {
                            return node;
                        }
                    });
                    System.out.println(root);
                }
            }
        }

        //平衡新插入的节点 红黑树处理插入有12中情况
        static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root,
                                                      TreeNode<K, V> x) {
            x.red = true;
            //xp表示父节点  xpp表示祖父节点  xppl表示祖父节点的左节点  xppr表示祖父节点右节点
            for (TreeNode<K, V> xp, xpp, xppl, xppr; ; ) {//非递归处理节点插入时,上溢的问题
                if ((xp = x.parent) == null) {
                    x.red = false;//父节点为空,直接将父节点染成黑色,并返回
                    return x;
                } else if (!xp.red || (xpp = xp.parent) == null)//父节点为黑色,不做处理
                    return root;
                if (xp == (xppl = xpp.left)) {//父节点是祖父节点的左边 xppl表示为当前节点的祖父节点
                    if ((xppr = xpp.right) != null && xppr.red) {//xppr为x的uncle节点,并且为红色
                        //处理为:将自己父节点、uncle节点染成黑色,祖父节点染成红色 , 并且赋给x 继续处理逻辑(递归逻辑 非递归化)
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    } else {//uncle不存在或者非红色   就是黑色
                        if (x == xp.right) {//父节点是祖先节点左边,当前节点是父节点的右边LR,需要将右边这种情况转换为 LL
                            root = rotateLeft(root, x = xp);//需要终止 所以需要将xp赋给x
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {//右旋 LL 父节点
                            xp.red = false;//父节点染尘黑色  类似于先将自己染成黑色 因为是先旋转 才染的色, 原来的x变为了父节点
                            if (xpp != null) {
                                xpp.red = true;//祖先节点染成红色
                                root = rotateRight(root, xpp);//祖先节点向右旋转
                            }
                        }
                    }
                } else {//父节点是祖父节点的右边
                    if (xppl != null && xppl.red) {
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    } else {
                        if (x == xp.left) {
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

        static <K, V> TreeNode<K, V> rotateLeft(TreeNode<K, V> root,
                                                TreeNode<K, V> p) {
            TreeNode<K, V> r, pp, rl;
            if (p != null && (r = p.right) != null) {
                if ((rl = p.right = r.left) != null)
                    rl.parent = p;
                if ((pp = r.parent = p.parent) == null)
                    (root = r).red = false;
                else if (pp.left == p)
                    pp.left = r;
                else
                    pp.right = r;
                r.left = p;
                p.parent = r;
            }
            return root;
        }

        static <K, V> TreeNode<K, V> rotateRight(TreeNode<K, V> root,
                                                 TreeNode<K, V> p) {
            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;
        }

        /**
         * @param k  表示待插入的节点的key
         * @param pk 红黑树节点的key
         * @return
         */
        public int tieBreakOrder(Object k, Object pk) {
            int d;
            if (k == null || pk == null ||
                    (d = k.getClass().getName()
                            .compareTo(pk.getClass().getName())) == 0) {//k或pk其中都不为null就用类名来进行比较
                d = System.identityHashCode(k) <= System.identityHashCode(pk) ? -1 : 1;//k不为null,pk为null //k为null , pk不为null
            }
            return d;
        }

        public int compareComparables(Class<?> kc, Object k, Object x) {
            return (x == null || x.getClass() != kc ? 0 : ((Comparable) k).compareTo(x));//判断
        }

        public Class<?> comparableClassFor(K k) {
            if (k instanceof Comparable) {
                return k.getClass();
            }
            return null;
        }

        public Node<K, V> find(int hash, K key) {
            return parent == null ? root() : find(hash, key, null);
        }

        private Node<K, V> find(int hash, K key, Class<?> kc) {
            TreeNode<K, V> p = this;
            do {
                int ph = p.hash;
                K pk = p.key;
                int cmp = 0;
                TreeNode<K, V> pr = p.right, pl = p.left;
                if (ph < hash) {
                    p = pr;
                } else if (ph > hash) {
                    p = pl;
                } else if (pl == null) {
                    p = pr;
                } else if (pr == null) {
                    p = pl;
                } else if (ph == hash && (key == p.key || (key != null && key.equals(pk)))) {
                    return p;
                } else if (kc != null
                        && ((kc = comparableClassFor(key)) != null)
                        && (cmp = compareComparables(kc, key, kc)) != 0) {
                    p = (cmp < 0) ? p.left : p.right;
                } else if ((p = (TreeNode<K, V>) pl.find(hash, key, kc)) != null) {
                    return p;
                } else {
                    p = pr;
                }
            } while (p != null);
            return null;
        }

        private TreeNode<K, V> root() {
            for (TreeNode<K, V> r = this; ; ) {
                if (r.parent == null) {
                    return r;
                }
                r = r.parent;
            }
        }

        /**
         * 出现了问题
         *
         * @param value
         * @return
         */
        public boolean containsValue(V value) {
            return (parent != null ? root() : this).innerContainsValue(value);
        }

        private boolean innerContainsValue(V value) {
            TreeNode<K, V> p = this;
            do {
                if (p.value == value || (value != null && value.equals(p.value))) return true;
            } while ((p = (TreeNode<K, V>) p.next) != null);
            return false;
        }

        /**
         * 删除当前节点
         *
         * @param map
         * @param tab
         * @param moved
         */
        public void removeTreeNode(HashMap<K, V> map, Node<K, V>[] tab, boolean moved) {
            //维护前驱和后继
        }

        public void split(HashMap<K, V> map, int newCap, int i, int oldCap) {

        }
    }


    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean containsKey(K key) {
        return getNode(hash(key), key) != null;
    }

    @Override
    public boolean containsValue(V value) {
        Node<K, V>[] tab;
        int n;
        if ((tab = table) != null && (n = table.length) != 0) {
            Node<K, V> first = null, e, next;
            for (int i = 0; i < n; i++) {
                if ((first = tab[i]) != null) {
                    //第一个元素的value是否等于value
                    if (first.value == value || (value != null && value.equals(first.value)))
                        return true;
                    if ((e = first.next) != null) {
                        if (first instanceof TreeNode)
                            return ((TreeNode) first).containsValue(value);
                        do {
                            if (first.value == value || (value != null && value.equals(first.value)))
                                return true;
                        } while ((e = e.next) != null);
                    }
                }
            }
        }
        return false;
    }

    @Override
    public V put(K key, V v) {
        return putVal(hash(key), key, v);
    }

    private V putVal(int hash, K key, V value) {
        Node<K, V>[] tab;//指向哈希表
        Node<K, V> p;//
        int n, index;
        if ((tab = table) == null || (n = table.length) == 0) {
            n = (tab = resize()).length;
        }
        //(n - 1) & hash
        if ((p = tab[index = (n - 1) & hash]) == null) {//当前索引所在的hash桶不存在数据
            tab[index] = newNode(hash, key, value, null);
        } else {
            Node<K, V> e;//用于存储需要覆盖的
            K k;//保存键值
            if ((k = p.key) == key || (k != null && k.equals(key)))
                e = p;
            else if (p instanceof TreeNode)//红黑树的元素插入方式
                e = ((TreeNode<K, V>) p).putVal(hash, k, value);
            else {
                for (int binCount = 1; ; binCount++) {//binCount单链表中元素个数
                    if ((e = p.next) == null) {//判断是否为链表尾部 , //e为空
                        p.next = new Node<K, V>(hash, key, value, null);
                        if (binCount >= TREEIfy_THRESHOLD) {//单链表中元素个数是否大于阈值
                            treeifyBin(tab, hash);//单链表转化为红黑树
                        }
                        break;
                    }
                    //判定两个key相等
                    if ((k = e.key) == key || (k != null && k.equals(key))) {
                        break;
                    }
                    p = e;
                }
            }
            if (e != null) {
                V oldValue = e.value;//保存旧值
                e.value = value;
                return oldValue;
            }
        }
        size++;//元素个数增加
        if (size >= threshold) {
            resize();
        }
        //涉及到需要扩容
        modCount++;
        return null;
    }

    protected Node<K, V> newNode(int hash, K key, V value, Node<K, V> next) {
        return new Node<>(hash, key, value, next);
    }

    private void treeifyBin(Node<K, V>[] tab, int hash) {
        int n, index;
        Node<K, V> e;//用于存储临时节点
        if (tab == null || (n = tab.length) < MIN_TREEIfy_CAPACITY) {
            resize();//重新计算容量
        } else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K, V> tl, hd;
            tl = hd = null;//tl tail表示尾部 , hd:head表示头部
            do {
                TreeNode<K, V> p = replacementTreeNode(e, null);
                if (hd == null) {
                    hd = p;
                } else {
                    tl.next = p;
                    p.pre = tl;
                }
                tl = p;//tl指向尾部
            } while ((e = e.next) != null);//判断
            TreeNode<K, V> curNode = hd;
            System.out.print("start");
            while (curNode != null) {
                System.out.print("->" + curNode + ":" + ((n - 1) & curNode.hash));
                curNode = (TreeNode<K, V>) curNode.next;
            }
            System.out.println();
            if ((tab[index] = hd) != null) {
                hd.treeify(tab);//树状化 红黑树化
            }
        }
    }

    private TreeNode<K, V> replacementTreeNode(Node<K, V> e, Node<K, V> next) {
        return new TreeNode<K, V>(e.hash, e.key, e.value, next);
    }

    //对得到的hashcode进行高位和低位异或计算
    public int hash(Object key) {
        int h;
        return (h = key.hashCode()) ^ (h >>> 16);
    }

    @Override
    public V remove(K key) {
        Node<K, V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;

    }

    //hashMap中删除的思想是先找到这个节点,再调用节点的删除逻辑
    private Node<K, V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean moved) {
        Node<K, V>[] tab;
        int n, index;
        if ((tab = table) != null && (n = table.length) != 0) {
            Node<K, V> e = null, p, node = null;/*node:保存需要刪除的节点*/
            if ((p = tab[index = (n - 1) & (hash = hash(key))]) != null) {
                if (p.hash == hash && (key == p.key || (key != null && key.equals(p.key))))
                    node = p;
                else if ((e = p.next) != null) {//下一个节点不为空
                    if (p instanceof TreeNode)
                        node = ((TreeNode) p).find(hash, key);
                    else {//为单链表
                        do {
                            if (e.hash == hash && (e.key == key || (key != null && key.equals(e.key)))) {
                                node = e;//要删除的节点
                                break;
                            }
                            p = e; //p表示记录前一个节点,作用是用于删除元素
                        } while ((e = e.next) != null);
                    }
                }
                //是否找到待删除的节点
                if (node != null &&
                        (!matchValue ||
                                (value == node.value ||
                                        (value != null && value.equals(node.value))))) {
                    if (node instanceof TreeNode) {
                        ((TreeNode) node).removeTreeNode(this, tab, moved);
                    } else if (node == p) {//第一个节点就是待删除的节点,同时不失TreeNode节点
                        tab[index] = node.next;
                    } else {//待删除的不是第一个节点
                        p.next = node.next;
                    }

                    size--;
                    modCount++;
                    return node;
                }
            }
        }
        return null;
    }

    @Override
    public V get(K key) {
        Node<K, V> e;
        return ((e = getNode(hash(key), key)) == null) ? null : e.value;
    }

    /**
     * @return
     */
    private Node<K, V> getNode(int hash, K key) {
        Node<K, V>[] tab;
        int n;
        if ((tab = table) != null && (n = table.length) != 0) {
            Node<K, V> e;
            if ((e = tab[(n - 1) & hash]) != null) {
                if (e.hash == hash && (key == null ? key == e.key : key.equals(e.key)))
                    return e;
                if ((e = e.next) != null) {//下一个节点不为空
                    if (e instanceof TreeNode)//红黑树搜索
                        return ((TreeNode<K, V>) e).find(hash, key);
                    do {//单链表搜索
                        if (e.hash == hash && (e.key == key || (key != null && key.equals(e.key))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
        }
        return null;
    }

    @Override
    public void clear() {
        int size;
    }

    @Override
    public void forEach(BiConsumer<? super K, ? super V> action) {
        Node<K, V>[] tab;
        int n;
        if ((tab = table) != null && (n = tab.length) != 0) {
            Node<K, V> e = null;
            for (int i = 0; i < n; i++) {
                if ((e = tab[i]) != null && e instanceof TreeNode) {
                    final TreeNode<K, V> finalE = (TreeNode<K, V>) e;
                    BinaryTrees.println(new BinaryTreeInfo() {
                        @Override
                        public Object root() {
                            return finalE;
                        }

                        @Override
                        public Object left(Object node) {
                            return finalE.left;
                        }

                        @Override
                        public Object right(Object node) {
                            return finalE.right;
                        }

                        @Override
                        public Object string(Object node) {
                            return finalE;
                        }
                    });
                }
            }
        }
    }


    public void display() {
        Node<K, V>[] tab;
        int n;
        if ((tab = table) != null && (n = tab.length) != 0) {
            Node<K, V> e = null;
            for (int i = 0; i < n; i++) {
                if ((e = tab[i]) != null && e instanceof TreeNode) {
                    final TreeNode<K, V> finalE = (TreeNode<K, V>) e;
                    BinaryTrees.println(new BinaryTreeInfo() {
                        @Override
                        public Object root() {
                            return finalE;
                        }

                        @Override
                        public Object left(Object node) {
                            return ((TreeNode) node).left;
                        }

                        @Override
                        public Object right(Object node) {
                            return ((TreeNode) node).right;
                        }

                        @Override
                        public Object string(Object node) {
                            return node;
                        }
                    });
                }
            }
        }
    }
}

此源码的编写是为了实践散列数据结构和更加深层次了解HashMap底层设计原理和设计思想
其中display方法是可以打印红黑树的结构图,如果需要具体源码,可以在评论区留言哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值