Java 集合学习笔记:HashMap - 迭代器

news2024/11/18 21:42:04

Java 集合学习笔记:HashMap - 迭代器

  • iterators
    • HashIterator
      • hasNext
      • nextNode
      • remove
    • KeyIterator
    • ValueIterator
    • EntryIterator
  • spliterators
    • HashMapSpliterator
      • getFence 获取拆分器的右边界
      • estimateSize 估计剩余元素的个数
    • KeySpliterator
      • 1. trySplit 尝试拆分
      • 2. forEachRemaining 遍历剩余元素
      • 3. tryAdvance 如果还有元素就消费一个
      • 4. characteristics 返回特征
    • ValueSpliterator
    • EntrySpliterator
  • 内部类
    • KeySet
    • Values
    • EntrySet
      • contains 是否包含给定对象
      • remove 移除给定对象
      • spliterator 返回可拆分迭代器
      • forEach 遍历元素
  • 参考资料

iterators

HashIterator 自己实现了 hasNextnextNoderemove 三个方法。(它没有实现 Iterator
因为是个抽象类,无法实例化,所以只能作为其他类的公共部分
三个子类 KeyIteratorValueIteratorEntryIterator 继承 HashIterator 并各自己实现自己的 next
共同实现了 Iterator 接口中的所有方法。

在这里插入图片描述

HashIterator

这段源码中亮点应该就是通过 if + do while 实现遍历的部分了吧。

abstract class HashIterator {
    Node<K,V> next;        // 下一次调用 nextNode 会返回的结点
    Node<K,V> current;     // 当前结点
    int expectedModCount;  // 预期修改次数。用于检测并发冲突
    int index;             // 当前索引
    
	// 初始化迭代器
    HashIterator() {
        expectedModCount = modCount; // 缓存修改次数
        Node<K,V>[] t = table;		 // 缓存哈希表
        current = next = null;		 // 当前、下一次将要返回的 entry 都置空
        index = 0;					 // 当前所在位置的索引放到 0
        // 如果哈希表不为空,先来到第一个结点
        // do-while 遍历哈希表,找到第一个结点,给 next。(它也是所在桶的第一个节点)
        if (t != null && size > 0) { // advance to first entry
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

hasNext

    public final boolean hasNext() {
        return next != null;
    }

nextNode

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        // 检测并发冲突
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // 检测将要返回的结点如果为空就抛锅。因为调用 nextNode() 前,我们通常是检测过 hasNext()的。
        if (e == null)
            throw new NoSuchElementException();
        // 1. 用即将返回的 e 更新 current 指针。
        // 2. if 这句就是在遍历链表
        // 2.1. 取出当前结点 current 的下一个结点给 next 指针。
        // 2.2. 如果 next 为 null 说明链表遍历完了。要是哈希表不为空,就继续遍历哈希表,找下一个桶。
        // 3. do-while 是遍历哈希表
        // 3.1. index 还没走到头,就一直取下一个索引对应的值来判断。找到非空的,就是 next 指针该有的值了。
        if ((next = (current = e).next) == null && (t = table) != null) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }

remove

	// remove 与 nextNode 成对调用。移除后 current 会被置 null
    public final void remove() {
    	// 拿到当前对象,并检测。如果为空抛锅。
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        // 并发冲突检测
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // current 被置 null,在下次调用 nextNode 前都无法再使用 remove
        current = null;
        // 拿到 key 调用底层 removeNode 方法
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        // 同步修改次数
        expectedModCount = modCount;
    }
}

KeyIterator

继承 HashIterator 重写 next 方法,返回具体的 key 值。

final class KeyIterator extends HashIterator implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

ValueIterator

继承 HashIterator 重写 next 方法,返回具体的 value 值。

final class ValueIterator extends HashIterator implements Iterator<V> {
    public final V next() { return nextNode().value; }
}

EntryIterator

继承 HashIterator 重写 next 方法,原样返回结点 Node,只不过返回类型改为接口 Map.Entry
NodeMap.Entry 的实现类:static class Node<K,V> implements Map.Entry<K,V> {...}

final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
   public final Map.Entry<K,V> next() { return nextNode(); }
}

spliterators

  1. 可拆分迭代器的区间定义是 [左边界索引, 右边界索引) 右闭右开。
  2. 初始时如:[0, arr.length]

HashMapSpliterator

作为其他几个可拆分迭代器的基类,实现了公用的方法。

static class HashMapSpliterator<K,V> {
    final HashMap<K,V> map;		// 临时变量缓存将被拆分的 HashMap m
    Node<K,V> current;          // 当前结点(下一次将要处理的结点)
    int index;                  // 左边界[包含] current 的索引, 在 advance/split 中被更改
    int fence;                  // 右边界(不包含)
    int est;                    // 元素个数(不是精确值,是个估值)
    int expectedModCount;       // 预期修改次数。用于检测并发冲突

	// (map, 0, -1, 0, 0);
    HashMapSpliterator(HashMap<K,V> m, int origin,
                       int fence, int est,
                       int expectedModCount) {
        this.map = m;
        this.index = origin;
        this.fence = fence; // 初始化 -1
        this.est = est;
        this.expectedModCount = expectedModCount;
    }

getFence 获取拆分器的右边界

获取拆分器的右边界。(如果还没有就先获取)

  1. 将一堆临时变量初始化赋值。(只有第一次调用时才会进入 if )
  2. 算出并返回右边界。
    final int getFence() {
        int hi;
        // fence 初始化 -1,所以第一次调用此方法是会进入 if
        if ((hi = fence) < 0) {
            HashMap<K,V> m = map;	// 取hashmap实例
            est = m.size;			// 获取元素个数
            expectedModCount = m.modCount;	// 同步计数:预期修改次数 = 修改次数
            Node<K,V>[] tab = m.table;	// 缓存哈希表
            hi = fence = (tab == null) ? 0 : tab.length; // 哈希表不为空返回长度。
        }
        return hi;
    }

estimateSize 估计剩余元素的个数

    public final long estimateSize() {
        getFence(); // 初始化
        return (long) est;
    }
}

KeySpliterator

static final class KeySpliterator<K,V> 
	extends HashMapSpliterator<K,V> 
	implements Spliterator<K> {
	
    /**
     * @param m					将要被拆分的 HashMap
     * @param origin			左边界
     * @param fence				右边界
     * @param est				剩余元素个数
     * @param expectedModCount	预期修改次数
     */
    KeySpliterator(HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) {
        super(m, origin, fence, est, expectedModCount);
    }

1. trySplit 尝试拆分

尝试拆分,如果可拆分,创建一个新的拆分器并返回。
注意:是按哈希表拆分,并不理会桶中结点的个数。
除非每个桶中都平均分配元素,否则 est >>>= 1 算出来的元素个数,肯定是不准确的。

  1. 算出左、中、右三个位置的索引。
  2. 判断如果还能拆分,就劈成两半,用左半边 [左,中) 创建一个新拆分器返回。
  3. 当前拆分器更新一下左边界,新范围[中,右)
    在这里插入图片描述
    public KeySpliterator<K,V> trySplit() {
    	// 获取右、左边界 hi 和 lo,算出中间位置 = (左 + 右) / 2
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        // 1. 左边界 >= 中间,表示已无可再分了。
        // 2. 正常情况下 current 为空,原因见 tryAdvance 部分。
        // 3. 新拆分器的范围[左,中),当前拆分器的左更新为中[中,右)
        //    new 的同时更新当前拆分器 index,est
        //    est >>>= 1 位运算结果作为参数。一步搞定当前和新拆分器的 size
        return (lo >= mid || current != null) ? null 
        		: new KeySpliterator<>(
        			map, lo, index = mid, est >>>= 1, expectedModCount
        		);
    }

2. forEachRemaining 遍历剩余元素

遍历每个剩余元素依次执行给定的操作 action,直到所有元素都处理完毕或该操作抛出异常。如果该SpliteratorORDERED,则按遇到顺序执行操作。动作抛出的异常被传递给调用者。

  1. 准备工作:声明临时变量。检测并发冲突。初始化。
  2. 哈希表有内容就进去遍历。消费每一个元素的 key
    public void forEachRemaining(Consumer<? super K> action) {
    	// i	临时变量用于缓存当前桶索引
    	// hi	临时变量用于缓存右边界
    	// mc	临时变量用于缓存预期修改次数
        int i, hi, mc;
        
        // 传进来的 Lambda 为空,直接抛锅。是个 null 让我怎么执行?
        if (action == null)
            throw new NullPointerException();
            
        HashMap<K,V> m = map;		// 临时变量缓存将被拆分的 HashMap。下面会用它来取 table、modCount
        Node<K,V>[] tab = m.table;	// 临时变量缓存此 hashmap 中的哈希表
        
        // 确保第一次调用时进入if。
        // 道理与 getFence 中 if ((hi = fence) < 0) 一样。 
        // 对比一下就能发现,这里是个简化了的 getFence() 
        if ((hi = fence) < 0) {
        	// 同步三个层级的:缓存预期修改次数
        	// 当前方法的 = 迭代器的 = hashMap的
            mc = expectedModCount = m.modCount;	
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
            
        // 符合条件就执行遍历
        // 1. 哈希表不为空,且长度 >= 右边界 (如果哈希表都没东西,就没必要遍历了)
        // 2. 左边界 >= 0 				   (索引值不可能 < 0)
        // 3. 当前索引 < 右边界 || 当前结点非空 
        //	(没有右边界,可以继续找剩余的桶,当前结点非空,可以消费之)
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            
            Node<K,V> p = current;
            current = null;
            
            // 真正开始遍历工作
            do {
            	// 如果结点 p 为null,说明链表走到头了,去下一个桶。
            	// 否则:先消费一下 key,再继续下一个结点。
            	// 继续条件:
            	// p != null 表示当前有结,可以继续。
            	// i < hi	 表示还未到达右边界,可以继续。
                if (p == null) 
                    p = tab[i++];
                else {
                    action.accept(p.key);
                    p = p.next;
                }
            } while (p != null || i < hi); 
            
            // 检测并发冲突
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

3. tryAdvance 如果还有元素就消费一个

如果存在剩余元素,则对其执行给定操作 action,返回true;否则返回false
如果该Spliterator为ORDERED,则操作将按遇到顺序对下一个元素执行。
action 抛出的异常被传递给调用者。
注意:只有 current == null,的情况下,才可以才分。因为它不为空,说明调用过 n 次 tryAdvance current 走到了链表中的某个结点。此时不允许拆分,只有处理完当前桶中所有节点,current 为 null 后才能拆分。

  1. 准备工作:声明临时变量。检测并发冲突。初始化。
  2. 哈希表非空,且长度 >= 右边界,且当前索引 >=0 遍历结点
  3. 找到下一个结果,找到就消费它。
    public boolean tryAdvance(Consumer<? super K> action) {
        int hi;
        // 传进来的 Lambda 为空,直接抛锅。是个 null 让我怎么执行?
        if (action == null)
            throw new NullPointerException();
            
        // 如果前面没报错,这里再声明临时变量拿哈希表。免得浪费资源。
        Node<K,V>[] tab = map.table;
        
        // 1. 哈希表不为空,且长度大于当前拆分器的右边界,且当前索引 >= 0
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
        	// current 不为空,则继续消费它。
        	// index < hi,表示当前还未走到右边界,可以继续。
            while (current != null || index < hi) {
            	// 如果 current 结点为 null,说明链表走到头了,去下一个桶看看。
            	// 否则,当前结点不为空继续遍历链表。
                if (current == null)
                    current = tab[index++]; // 取下一个桶的首结点。(同时还更新了index)
                else {
                    K k = current.key;		// 取出当前结点的 key
                    current = current.next; // 遍历到下一个结点。
                    action.accept(k);		// 消费 key
                    // 检测并发冲突
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true; // 消费成功返回 true
                }
            }
        }
        return false;
    }

4. characteristics 返回特征

返回该Spliterator及其元素的一组特征。
这里用的是位运算|,8个特性每个特性占一位,可以组合。参考:《Java8实战》读书笔记06:Parallel Stream 并行流 - 表7-2 Spliterator的特性
在给定的分割器上重复调用characteristics(),在调用trySplit之前或中间,应该总是返回相同的结果。否则不能保证使用该Spliterator进行的任何计算。

public int characteristics() {
    return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
        Spliterator.DISTINCT;
}

ValueSpliterator

逻辑与 KeySpliterator 基本相同,只是把消费对象从 key 改为了 value
在这里插入图片描述

static final class ValueSpliterator<K,V> 
	extends HashMapSpliterator<K,V> 
	implements Spliterator<V> 
{

    ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) {
        super(m, origin, fence, est, expectedModCount);
    }

    public ValueSpliterator<K,V> trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid || current != null) ? null :
            new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
                                      expectedModCount);
    }

    public void forEachRemaining(Consumer<? super V> action) {
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        if ((hi = fence) < 0) {
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            Node<K,V> p = current;
            current = null;
            do {
                if (p == null)
                    p = tab[i++];
                else {
                    action.accept(p.value);
                    p = p.next;
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    public boolean tryAdvance(Consumer<? super V> action) {
        int hi;
        if (action == null)
            throw new NullPointerException();
        Node<K,V>[] tab = map.table;
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
            while (current != null || index < hi) {
                if (current == null)
                    current = tab[index++];
                else {
                    V v = current.value;
                    current = current.next;
                    action.accept(v);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }

    public int characteristics() {
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
    }
}

EntrySpliterator

逻辑与 KeySpliterator 基本相同,只是把消费对象从 key 改为了 entry
在这里插入图片描述

static final class EntrySpliterator<K,V>
    extends HashMapSpliterator<K,V>
    implements Spliterator<Map.Entry<K,V>> {
    
    EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) {
        super(m, origin, fence, est, expectedModCount);
    }

    public EntrySpliterator<K,V> trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid || current != null) ? null :
            new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
                                      expectedModCount);
    }

    public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        if ((hi = fence) < 0) {
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            Node<K,V> p = current;
            current = null;
            do {
                if (p == null)
                    p = tab[i++];
                else {
                    action.accept(p);
                    p = p.next;
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
        int hi;
        if (action == null)
            throw new NullPointerException();
        Node<K,V>[] tab = map.table;
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
            while (current != null || index < hi) {
                if (current == null)
                    current = tab[index++];
                else {
                    Node<K,V> e = current;
                    current = current.next;
                    action.accept(e);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }

    public int characteristics() {
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
            Spliterator.DISTINCT;
    }
}

内部类

KeySet

final class KeySet extends AbstractSet<K> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<K> iterator()     { return new KeyIterator(); }
    public final boolean contains(Object o) { return containsKey(o); }
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    // 返回 key可拆分迭代器
    public final Spliterator<K> spliterator() {
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    // 遍历整个集合,对每个key 执行 action
    // size > 0 且哈希表为不为空就遍历集合。
    public final void forEach(Consumer<? super K> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
            
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            // 外层 i 是索引,没到哈希表的尽头就继续
            // 内层 当前结点 e 不为空就继续 e = e.next            
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.key); // 对 key 执行 action
            }
            // 检测并发冲突
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

Values

    final class Values extends AbstractCollection<V> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<V> iterator()     { return new ValueIterator(); }
        public final boolean contains(Object o) { return containsValue(o); }
        public final Spliterator<V> spliterator() {
            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super V> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.value);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }

EntrySet

final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<Map.Entry<K,V>> iterator() {
        return new EntryIterator();
    }
    ...
}    

contains 是否包含给定对象

  1. 首先检测指定对象类型是否 Map.Entry 实例。不事就肯定 false 了
  2. 类型对的上,强转为 Map.Entry<?,?> 方便接下来的操作。
  3. 从给定对象中取出 key,再用这个 key 从 hashmap 中取结点(Entry)出来。
  4. 如果取到 Entry,且 equals 相等,说明找到目标。返回 true
    public final boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>) o;
        Object key = e.getKey();
        Node<K,V> candidate = getNode(hash(key), key);
        return candidate != null && candidate.equals(e);
    }

remove 移除给定对象

  1. 检测给定对象,类型符合就取出 key、value
  2. 调用 removeNode 移除结点。要求 key、value 都匹配才移除。
  3. removeNode 会返回被移除的结点,如果不为空,说明移除成功。
    public final boolean remove(Object o) {
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Object value = e.getValue();
            return removeNode(hash(key), key, value, true, true) != null;
        }
        return false;
    }

spliterator 返回可拆分迭代器

    public final Spliterator<Map.Entry<K,V>> spliterator() {
        return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }

forEach 遍历元素

逻辑跟 KeySet 一样。就强调一个关键部分:

  1. 两层 for 外层走索引,遍历数组。
  2. 内层走 next 遍历链表。
    public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

参考资料

笑虾:Java 集合学习笔记:HashMap
笑虾:《Java8实战》读书笔记06:Parallel Stream 并行流 - 表7-2 Spliterator的特性
笑虾:《Java8实战》读书笔记06:Parallel Stream 并行流 - 7.3 Spliterator 可分迭代器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AQS学习

1.1 AQS 简单介绍 AQS 的全称为&#xff08;AbstractQueuedSynchronizer&#xff09;&#xff0c;这个类在 java.util.concurrent.locks 包下面。 AQS 是一个用来构建锁和同步器的框架&#xff0c;使用 AQS 能简单且高效地构造出应用广泛的大量的同步器&#xff0c; 比如我们提…

多协议标签交换MPLS(计算机网络-网络层)

目录 MPLS 的优势 MPLS 首部的位置与格式 MPLS 首部的位置与格式 MPLS 转发等价类 MPLS 的优势 MPLS 的真正优点在于它的流量管理能力&#xff1a;提供沿多条路径转发分组的能力&#xff0c;并能灵活地为某些流量指定其中的一条路径 这种能力被称为显示路由&#xff0c;其…

占道经营出店摆摊监测识别 python

占道经营出店摆摊监测识别通过python基于yolov7网络架构深度学习模型&#xff0c;对现场画面中检测到出店摆摊违规经营或者流动商贩占道经营时&#xff0c;立即抓拍告警同步后台。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV…

【Django】第四课 基于Django超市订单管理系统开发

概念 本文在上一文之上&#xff0c;针对管理员&#xff0c;经理&#xff0c;普通员工身份的用户操作订单管理模块功能。 功能实现 该功能也是业务功能模块&#xff0c;管理员不具备操作权限&#xff0c;普通员工需要对超市所合作的供应商进行进货&#xff0c;因此普通员工可…

数据结构与算法——Java实现排序算法(二)

数据结构与算法——Java实现排序算法&#xff08;一&#xff09;_我爱布朗熊的博客-CSDN博客 七、希尔排序&#xff08;自我感觉有点难理解&#xff09; 为了解决直接插入排序所带来的弊端&#xff0c;我们接来下看一下希尔排序 希尔排序也是一种插入排序&#xff0c;简单插入排…

口罩佩戴监测系统 yolo

口罩佩戴监测系统通过yolo网络对现场画面人员口罩佩戴情况进行识别检测。我们使用YOLO(你只看一次)算法进行对象检测。YOLO是一个聪明的卷积神经网络(CNN)&#xff0c;用于实时进行目标检测。该算法将单个神经网络应用于完整的图像&#xff0c;然后将图像划分为多个区域&#x…

科技交流英语(2022秋)Unit 5 test

科技交流英语&#xff08;2022秋&#xff09;Unit 5 test 简介 由电子科技大学组织开设&#xff0c;授课教师为李京南、庞慧、刘兆林等5位老师。 课程介绍 英语广泛用于工程技术领域的国际交流。如何使用简洁的语言清楚地传递信息是工程师在国际舞台上常常面临的问题。本课…

ARM S5PV210 串行通信编程实战

一、串行通信编程实战1 1、整个程序流程分析 (1) 整个串口通信相关程序包含 2 部分&#xff1a;uart_init 负责初始化串口&#xff0c;uart_putc 负责发送一个字节。 2、串口控制器初始化关键步骤 (1) 初始化串口的 Tx 和 Rx 引脚所对应的GPIO&#xff08;查原理图可知 Rx 和…

【数列分段DP】膜拜

P1564 膜拜 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a; 思路&#xff1a; 这是个经典模型&#xff1a;数列分段DP&#xff0c;在其他地方也出现过&#xff1a;(150条消息) 代码源每日一题div1 DP 数组划分_lamentropetion的博客-CSDN博客 这类DP模型核…

_13LeetCode代码随想录算法训练营第十三天-C++二叉树

_13LeetCode代码随想录算法训练营第十三天-C二叉树 题目列表 102.二叉树的层序遍历107.二叉树的层次遍历II199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个右侧节点指针117.填充每个节点的下一个右侧节点指针II…

说话人识别中的数据预处理和数据增强

数据预处理 假设已经采集到一些数据&#xff0c;在进行训练之前&#xff0c;需要先对数据做以下预处理&#xff1a; 数据清洗语音检测&#xff08;Voice Activity Detection&#xff0c;VAD&#xff0c;也叫Speech Detection&#xff0c;或Silence Suppression&#xff0c;静音…

ICMP V6(计算机网络-网络层)

IPv6 使用的 ICMP IETF 制定的与IPv6配套使用的ICMP新版本&#xff0c;即ICMPv6 ICMPv6报文作为IPv6分组有效载荷进行传输&#xff0c;对应的IPv6“下一个首部字段”的值为58 ICMPv6 的报文格式和 IPv4 使用的 ICMP 的相似&#xff0c;即前 4 个字节的字段名称都是一样的&…

Linux下进程及其相关概念理解

目录 何为进程&#xff1f; task_struct 中存储了什么进程信息&#xff1f; 如何查看进程&#xff1f; 如何获取进程pid&#xff1f; 如何创建子进程&#xff1f; 为什么返回值如此呢&#xff1f; 为什么有两个返回值&#xff1f; 进程状态 进程的一般状态 运行态 终…

18-JavaSE基础巩固练习:正则表达式练习

正则表达式基本练习 一、练习1 1、需求 请编写正则表达式验证用户输入的手机号码是否满足要求。请编写正则表达式验证用户输入的邮箱号是否满足要求。请编写正则表达式验证用户输入的电话号码是否满足要求。 2、思路&#xff1a; 心得&#xff1a; 拿着一个正确的数据&…

SAP UI5 Smart Chart 功能介绍

笔者已经写过一篇详细的文章介绍 SAP UI5 Smart Chart 的使用方法&#xff1a; SAP UI5 应用开发教程之一百五十三 - 使用 SAP UI5 Smart Chart 控件轻松绘制十数种不同类型的专业图表 本文泛泛地介绍 Smart Chart 提供的一些其他功能。 工具栏右侧的按钮可用于选择图表类型…

Zookeeper 1 初识 Zookeeper 1.1 Zookeeper 概念

Zookeeper 【黑马程序员Zookeeper视频教程&#xff0c;快速入门zookeeper技术】 文章目录Zookeeper1 初识 Zookeeper1.1 Zookeeper 概念1.1.1 Zookeeper 概念1 初识 Zookeeper 1.1 Zookeeper 概念 1.1.1 Zookeeper 概念 Zookeeper 是 Apache Hadoop 项目下的一个子项目&…

3、前端笔记-JS-变量

1、什么是变量 变量是用于存放数据的容器&#xff0c;可以通过变量名获取数据 本质&#xff1a;变量是程序在内存中申请的一块用来存放数据的空间 2、变量的使用 2.1 声明变量和赋值 1、声明变量 2、给变量赋值 var:JS关键字&#xff0c;用来声明变量。使用这个关键字后&a…

(二)计算机组成原理——计算机的基本组成

目录 冯诺依曼计算机的特点 计算机硬件框图 系统复杂性管理的方法&#xff08;3’Y&#xff09; 计算机的工作步骤 上机前的准备 计算机的工作过程 存储器的基本组成 运算器的基本组成及操作过程 控制器 计算机组成原理课程笔记。 冯诺依曼计算机的特点 冯诺依曼计算…

sandbox启动未加载repeater的问题

背景 通过官方提供的 repeater 的下载链接&#xff0c;并不能够在sandbox启动时&#xff0c;加载进行&#xff0c;我们可以看下sandbox的日志截图 但是如果通过源码的repeater进行安装后&#xff0c;就能够成功加载到repeater。 分析 这是个很奇怪的问题&#xff0c;想要分析…

Sentinel的规则

四.Sentinel的规则 1.流控规则 1.1流控规则有哪些? 流量控制有以下几个角度: 资源的调用关系&#xff0c;例如资源的调用链路&#xff0c;资源和资源之间的关系&#xff1b;运行指标&#xff0c;例如 QPS&#xff08;每秒查询率&#xff09;、线程池、系统负载等&#xff…