主頁 >  其他 > HashMap設計原理與實作(下篇)200行帶你寫自己的HashMap!!!

HashMap設計原理與實作(下篇)200行帶你寫自己的HashMap!!!

2022-07-12 19:18:08 其他

HashMap設計原理與實作(下篇)200行帶你寫自己的HashMap!!!

我們在上篇文章哈希表的設計原理當中已經大體說明了哈希表的實作原理,在這篇文章當中我們將自己動手實作我們自己的HashMap,完整的代碼在文章末尾,

在本篇文章當中主要通過線性探測法,從最基本的陣列再到HashMap當中節點的設計,一步一步的實作一個能夠實作KeyValue映射的容器,寫出我們自己的哈希表MyHashMap,讓可以具備HashMap最常見的兩個功能,putget方法,

我們的陣列當中應該存盤什么樣資料?

在上篇哈希表的設計原理當中我們已經仔細說明,在HashMap當中我們是使用陣列去存盤具體的資料的,那么在我們的陣列當中應該存盤什么樣的資料呢?假設在HashMap的陣列當中存盤的資料型別為Node,那么這個類需要有哪些欄位呢?

  • 首先一點我們肯定需要存盤Value值,因為我們最終需要通過get方法從HashMap當中取出我們所需要的值,

  • 第二點當我們通過get方法去取值的時候是通過Key(鍵值)去取的,當哈希值產生沖突的時候,我們不僅需要通過哈希值確定位置,還需要通過比較通過函式get傳遞的Key和陣列當當中存盤的資料的key是否相等,因此我們需要存盤鍵值Key

  • 第三點為了避免重復計算哈希值(因為有的物件的哈希值計算還是比較費時間),我們可以使用一個欄位去存盤計算好的哈希值,

根據以上三點我們的Node類的設計如下:

  private static class Node<K, V> {
    /**
     * 用于存盤我們計算好的 key 的哈希值
     */
    final int hash;

    /**
     * Key Value 中的 Key 物件
     */
    final K key;

    /**
     * Key Value 中的 Value 物件
     */
    V value;

    /**
     * hash 是鍵值 key 的哈希值  key 是鍵 value 是值
     * @param hash
     * @param key
     * @param value
     */
    public Node(int hash, K key, V value) {
      this.hash = hash;
      this.key = key;
      this.value = https://www.cnblogs.com/Chang-LeHung/p/value;
    }

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

    @Override
    public String toString() {
      return key +"=" + value;
    }
  }

我們的陣列長度應該怎么設定?

在討論這個問題之前我們首先來回歸一下位運算的操作,在計算機當中資料都是二進制存盤那么二進制運算是如何操作的呢?

int a = 7;
int b = 3;
System.out.println(a & b); // 輸出結果為3

上述代碼的位運算操作如下:

進行位運算是,二進制數的對應位置進行相應的操作,&運算的結果只有兩個位元位的資料都是1時,運算結果才等于1,其余的情況都為0,因此3 & 7 = 3

通過put函式放入HashMap當中的資料首先需要將key的哈希值與陣列的長度進行取余運算得到對應的下標,再將資料放入到陣列對應的下標當中,但是在實際的操作當中我們將底層陣列的長度設定為2的整數次冪,并且使用位運算&去進行取余數操作,而這樣操作主要有一下三點原因:

  • 位運算&的程式執行效率,比取余%操作更加高效,需要的時間越短,
  • 當陣列的長度為2的整數次冪的時候,得到的下標越均勻,造成的哈希沖突更少,
  • 任何一個數a\(2^n\)取余數得到的結果與a\(2^n - 1\)進行&操作結果是相等的,即:

\[a \% 2^n = a \& (2^n - 1) \]

我們現在先來解釋一下第三點,令a = 127n = 4,那么:127%16 = 15127 & 15 = 15

首先我們需要明白求余數的意義是什么?12716取余,就是用127一直減去16,直到某個結果小于16為止,得到的值為求余數結果,

上圖當中紅框當中包括的位置就是小于16的部分

上圖當中紅框框住的部分是16的倍數,因此在求余數的時候上圖當中紅框框住的部分就沒有了,都為0,只會剩下小于16的部分,這跟15進行&操作得到的結果是一致的,這也就解釋了上面提到的第三條,

在第二條中我們提到了使用陣列長度為2的整數次冪可以在一定程度上減少哈希沖突,因為進行下標運算的時候是與\(2^n - 1\)進行&操作,而\(2^n - 1\)的二進制表示最后一部分位置上的數都是1

比如下圖中的資料都是2的整數次冪減一之后的結果:

RoundUp函式

因為我們需要陣列的長度是2的整數次冪,而我們之后在初始化函式當中會允許用戶輸入一個陣列長度的大小,但是用戶輸入的數字可能不是2的整數次冪,因此我們需要將用戶輸入的資料變成2的整數次冪,我們可以將用戶輸入的資料變成大于等于這個數的最小的2的整數次冪,

比如說如果用戶輸入的是12我們需要將其變成16,如果輸入的是28我們需要將其變成32,我們可以通過下面這個函式做到這一點:

  /**
   * 回傳第一個大于或者等于 capacity 且為 2 的整數次冪的那個數
   * @param capacity
   * @return
   */
  static int roundUp(int capacity) {
    int n = capacity - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    // 如果最終得到的資料小于 0 則初始長度為 1
    // 如果長度大于我們所允許的最大的容量 則將初始長度設定為我們
    // 所允許的最大的容量
    // MAXIMUM_CAPACITY = 1 << 30;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
  }

上面的代碼還是很難理解的,讓我們一點一點的來分析,首先我們使用一個2的整數次冪的數進行上面移位操作的操作!

從上圖當中我們會發現,我們咋一個數的二進制數的32位放一個1,經過移位之后最終32位的位元數字全部變成了1,根據上面數字變化的規律我們可以發現,任何一個位元經過上面移位的變化,這個位元后面的31個位元位都會變成1,像下圖那樣:

因此上述的移位操作的結果只取決于最高一位的位元值為1,移位操作后它后面的所有位元位的值全為1,而在上面函式的最后,如果最終的容量沒有大于我們設定的最大容量MAXIMUM_CAPACITY,我們回傳的結果就是上面移位之后的結果 +1,又因為移位之后最高位的1到最低位的1之間的位元值全為1,當我們+1之后他會不斷的進位,最終只有一個位元位置是1,因此它是2的整數倍,

roundUp函式當中,給初始容量減了個1,這樣做的原因是讓這個函式的回傳值大于等于傳入的引數capacity

roundUp(4) == 4 // 就是當傳入的資料已經是 2 的整數次冪的時候也回傳傳入的值
roundUp(3) == 4
roundUp(5) == 8

哈希函式

  /**
   * 這個 key 是 put 函式傳進來的 key
   * @param key
   * @return
   */
  static int hash(Object key) {
    int h;
    // 呼叫物件自己實作的 hashCode 方法
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

上面的函式之所以要將物件的哈希值右移16,是因為我們的陣列的長度一般不會超過\(2^{16}\),因為\(2^{16}\)已經是一個比較大的值了,因此當哈希值與\(2^n - 1\)進行&操作的時候,高位通常沒有使用到,這樣做的原理是可以充分利用資料哈希值當中的資訊,

擴容機制

當我們一直往HashMap加入資料的話,陣列遲早會被用完,當陣列用完之后我們就需要進行擴容,我們要記住一點擴容之后的陣列長度也需要滿足2的整數次冪,因為上面我們已經提到陣列的長度需要是2的整數次冪,因此擴容之后的長度也需要保持是2的整數次冪,

但是在實際情況當中我們并不是當陣列完全被使用完之后才進行擴容,因為如果陣列快被使用完之后,再加入資料產生哈希沖突的可能性就會很大,因此我們通常會設定一個負載因子(load factor),當陣列的使用率超過這個值的時候就進行擴容,即當(陣列長度為L,陣列當中資料個數為S,負載因子為F):

\[S \ge L \times F \]

Java代碼實作HashMap

先看一下我們需要的欄位

public class MyHashMap<K, V> {

  /**
   * 默認陣列的容量
   */
  static final int DEFAULT_CAPACITY = 16;

  /**
   * 默認負載因子
   */
  static final float DEFAULT_LOAD_FACTOR = 0.75f;

  /**
   * 哈希表中陣列的最大長度
   */
  static final int MAXIMUM_CAPACITY = 1 << 30;

  /**
   * 真正存盤資料的陣列
   */
  Node<K, V>[] hashTable;

  /**
   * 哈希表陣列當中存盤的資料的個數
   */
  int size;

  /**
   * 哈希表當中的負載因子
   */
  float loadFactor = 0.75f;

  /**
   * 哈希表擴容的閾值 = 哈希表的長度 x 負載因子
   * 當超過這個值的時候進行擴容
   */
  int threshold;
}

put函式的實作

public void put(K key, V value) {

    if (null == key)
        throw new RuntimeException("哈希表的鍵值不能為空");
    int hash = hash(key);
    // 這表示使用了空參建構式 而且是第一次呼叫 put 函式
    if (null == hashTable) {
        hashTable = (Node<K, V>[]) new Node[DEFAULT_CAPACITY];
        // 計算陣列長度對應的擴容閾值
        threshold = (int)(hashTable.length * loadFactor);
        // 進行 & 運算得到資料下標
        hashTable[hash & (DEFAULT_CAPACITY - 1)] = new Node<K, V>(hash, key, value);
    }else {
        int n = hashTable.length;
        int idx = hash & (n - 1);
        // 如果陣列不為空 說明已經有資料存在了
        // 如果哈希值相同的話說明是同一個物件了
        // 也可以進行跳出
        while (null != hashTable[idx] && !key.equals(hashTable[idx].key))
            idx = (idx + 1) & (n - 1);
        // 如果是通過 null != hashTable[idx] 條件跳出則是新添加資料
        // 如果是通過 !key.equals(hashTable[idx].key) 條件跳出
        // 則是因為 put 函式的 key 已經存在這次操作是更新資料
        hashTable[idx] = new Node<K, V>(hash, key, value);
    }
    // 如果陣列當中使用過的資料超過閾值
    if (++size > threshold) {
        resize();
    }
}

擴容(resize函式)實作

  /**
   * 如果你已經看懂 put 函式的代碼,那這個代碼就比較簡單了
   * 因為只是單純的將原陣列的資料重新進行哈希并且加入到新陣列
   */
  private void resize() {
    int n = (hashTable.length << 1);
    threshold = (int) (n * loadFactor);
    Node<K, V>[] oldTable = hashTable;
    hashTable = (Node<K, V>[]) new Node[n];
    for (int i = 0; i < oldTable.length; i++)  {
      if (null == oldTable[i])
        continue;
      Node<K, V> node = oldTable[i];
      int idx = node.hash & (n - 1);
      while (null != hashTable[idx] && !node.key.equals(hashTable[idx].key))
        idx = (idx + 1) & (n - 1);
      hashTable[idx] = node;
    }
  }

get函式實作

public V get(K key) {
    if (null == key)
        throw new RuntimeException("查詢的鍵值不能為空");
    int hash = hash(key);
    int n = hashTable.length;
    int idx = hash & (n - 1);
    if (null == hashTable[idx])
        return null;
    // 這里同樣需要進行線性探測
    // 因為哈希值相同時 key 值
    // 不一定相同 因為會有哈希沖突
    for (;;) {
        // 當陣列當中的資料不為空
        // 且資料當中的哈希值和傳入 key
        // 的哈希值相等 而且鍵值相等
        // 就是找到了對應的資料
        if (null != hashTable[idx]
            && hash == hashTable[idx].hash
            && key.equals(hashTable[idx].key))
            break;
        idx = (idx + 1) & (n -1);
    }
    return hashTable[idx].value;
}

完整代碼

import java.util.*;

public class MyHashMap<K, V> {

  /**
   * 默認陣列的容量
   */
  static final int DEFAULT_CAPACITY = 16;

  /**
   * 默認負載因子
   */
  static final float DEFAULT_LOAD_FACTOR = 0.75f;

  /**
   * 哈希表中陣列的最大長度
   */
  static final int MAXIMUM_CAPACITY = 1 << 30;

  /**
   * 真正存盤資料的陣列
   */
  Node<K, V>[] hashTable;

  /**
   * 哈希表陣列當中存盤的資料的個數
   */
  int size;

  /**
   * 哈希表當中的負載因子
   */
  float loadFactor = 0.75f;

  /**
   * 哈希表擴容的閾值 = 哈希表的長度 x 負載因子
   * 當超過這個值的時候進行擴容
   */
  int threshold;

  /**
   * 回傳第一個大于或者等于 capacity 且為 2 的整數次冪的那個數
   * @param capacity
   * @return
   */
  static int roundUp(int capacity) {
    int n = capacity - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    // 如果最終得到的資料小于 0 則初始長度為 1
    // 如果長度大于我們所允許的最大的容量 則將初始長度設定為我們
    // 所允許的最大的容量
    // MAXIMUM_CAPACITY = 1 << 30;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
  }

  private static class Node<K, V> {
    /**
     * 用于存盤我們計算好的 key 的哈希值
     */
    final int hash;

    /**
     * Key Value 中的 Key 物件
     */
    final K key;

    /**
     * Key Value 中的 Value 物件
     */
    V value;

    /**
     * hash 是鍵值 key 的哈希值  key 是鍵 value 是值
     * @param hash
     * @param key
     * @param value
     */
    public Node(int hash, K key, V value) {
      this.hash = hash;
      this.key = key;
      this.value = https://www.cnblogs.com/Chang-LeHung/p/value;
    }

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

    @Override
    public String toString() {
      return key +"=" + value;
    }
  }

  public MyHashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
  }

  public MyHashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity <= 0) {
      throw new RuntimeException("初始化長度不能小于0");
    }
    if (initialCapacity > MAXIMUM_CAPACITY)
      initialCapacity = MAXIMUM_CAPACITY;

    initialCapacity = roundUp(initialCapacity);
    hashTable = (Node<K, V>[]) new Node[initialCapacity];
    this.loadFactor = loadFactor;
    threshold = (int) (loadFactor * initialCapacity);
  }

  public MyHashMap(int capacity) {
    this(capacity, DEFAULT_LOAD_FACTOR);
  }

  static int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

  public void put(K key, V value) {

    if (null == key)
      throw new RuntimeException("哈希表的鍵值不能為空");
    int hash = hash(key);

    if (null == hashTable) {
      hashTable = (Node<K, V>[]) new Node[DEFAULT_CAPACITY];
      threshold = (int)(hashTable.length * loadFactor);
      hashTable[hash & (DEFAULT_CAPACITY - 1)] = new Node<K, V>(hash, key, value);
    }else {
      int n = hashTable.length;
      int idx = hash & (n - 1);
      while (null != hashTable[idx] && !key.equals(hashTable[idx].key))
        idx = (idx + 1) & (n - 1);
      hashTable[idx] = new Node<K, V>(hash, key, value);
    }
    if (++size > threshold) {
      resize();
    }
  }

  private void resize() {
    int n = (hashTable.length << 1);
    threshold = (int) (n * loadFactor);
    Node<K, V>[] oldTable = hashTable;
    hashTable = (Node<K, V>[]) new Node[n];
    for (int i = 0; i < oldTable.length; i++)  {
      if (null == oldTable[i])
        continue;
      Node<K, V> node = oldTable[i];
      int idx = node.hash & (n - 1);
      while (null != hashTable[idx] && !node.key.equals(hashTable[idx].key))
        idx = (idx + 1) & (n - 1);
      hashTable[idx] = node;
    }
  }

  public V get(K key) {
    if (null == key)
      throw new RuntimeException("查詢的鍵值不能為空");
    int hash = hash(key);
    int n = hashTable.length;
    int idx = hash & (n - 1);
    if (null == hashTable[idx])
      return null;
    for (;;) {
      if (null != hashTable[idx]
          && hash == hashTable[idx].hash
          && key.equals(hashTable[idx].key))
        break;
      idx = (idx + 1) & (n -1);
    }
    return hashTable[idx].value;
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("[");
    for (int i = 0; i < hashTable.length; i++) {
      builder.append(hashTable[i]);
      builder.append(", ");
    }
    builder.delete(builder.length() - 2, builder.length());
    builder.append("]");
    return builder.toString();
  }
}

測驗我們自己實作的HashMap

測驗代碼:

public static void main(String[] args) {

    MyHashMap<String, Integer> map = new MyHashMap<>();
    Random random = new Random();
    HashMap<String, Integer> map1 = new HashMap<>();
    ArrayList<String> strs = new ArrayList<>();
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 1000000; i++) {
        String s = UUID.randomUUID().toString().substring(0, 4);
        int n = random.nextInt(1000);
        strs.add(s);
        list.add(n);
    }
    System.out.println("開始測驗插入 put 函式");
    long start = System.currentTimeMillis();
    for (int i = 0; i < list.size(); i++) {
        map1.put(strs.get(i), list.get(i));
    }
    long end = System.currentTimeMillis();
    System.out.println("HashMap:花費的時間 = " + (end - start));

    start = System.currentTimeMillis();
    for (int i = 0; i < list.size(); i++) {
        map.put(strs.get(i), list.get(i));
    }
    end = System.currentTimeMillis();
    System.out.println("MyHashMap:花費的時間 = " + (end - start));

    System.out.println("開始測驗查找 get 函式");

    start = System.currentTimeMillis();
    for (int i = 0; i < list.size(); i++) {
        map.get(strs.get(i));
    }
    end = System.currentTimeMillis();
    System.out.println("MyHashMap:花費的時間 = " + (end - start));

    start = System.currentTimeMillis();
    for (int i = 0; i < list.size(); i++) {
        map1.get(strs.get(i));
    }
    end = System.currentTimeMillis();
    System.out.println("HashMap:花費的時間 = " + (end - start));
}

輸出結果:

開始測驗插入 put 函式
HashMap:花費的時間 = 232
MyHashMap:花費的時間 = 324
開始測驗查找 get 函式
MyHashMap:花費的時間 = 186
HashMap:花費的時間 = 222

從上面的結果可以看出來我們自己實作的HashMap在插入資料的時候花費的時間比較長,JDKHashMap使用的是鏈地址法,他們擴容的次數肯定會比我們少,因為我們一個位置只能放一個資料,而JDK的能放多個,

但是我們查找的時候效率會高一點,因為JDK的哈希表還涉及鏈表的操作(可能還涉及紅黑樹),因此我們的效率可能會高一點,

總結

在本篇文章當中我們自己實作了一個線性探測的哈希表,但是我們并沒有實作remove函式,大家可以自己去實作這個函式,也不太困難!!!整篇文章的內容主要包含以下內容:

  • 節點Node的設計,
  • 陣列長度的設計,
  • roundUp函式設計,
  • 哈希函式的設計,
  • 擴容機制,

本篇內容還是比較多的,希望大家有所識訓,我是LeHung,我們下期再見!!!

更多精彩內容合集可訪問:https://github.com/Chang-LeHung/CSCore

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/498639.html

標籤:其他

上一篇:protobuf 的交叉編譯使用(C++)

下一篇:leetcode 455. Assign Cookies 分發餅干(簡)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more