Java哈希表HashTable(又稱散列表)的實(shí)現(xiàn)及練習(xí)題
一、概念
關(guān)鍵詞及重點(diǎn):【 哈希函數(shù)(散列函數(shù))、哈希沖突、負(fù)載因子、哈希桶 】
1.1 哈希表概念
用順序結(jié)構(gòu)或平衡樹(shù)存放數(shù)據(jù)時(shí),需要查找的時(shí)候必須經(jīng)過(guò)關(guān)鍵碼的多次比較,順序查找的時(shí)間復(fù)雜度為 O(N),平衡樹(shù)時(shí)間復(fù)雜度為樹(shù)的高度,即 O(log?N)。
但理想的搜索方法是不經(jīng)過(guò)任何比較,一次直接從表中得到想要搜索的元素。而哈希表(或者稱散列表)結(jié)構(gòu)就是通過(guò)哈希(散列)函數(shù)使元素的存儲(chǔ)位置與它的關(guān)鍵碼之間建立一一映射的關(guān)系,從而在查找時(shí)不用進(jìn)行比較,時(shí)間復(fù)雜度為 O(1)。當(dāng)向該結(jié)構(gòu)中:
插入元素:根據(jù)待插入元素的關(guān)鍵碼,以哈希函數(shù)計(jì)算出該元素的存儲(chǔ)位置并按此位置進(jìn)行存放;
搜索元素:對(duì)元素的關(guān)鍵碼進(jìn)行同樣的計(jì)算,把求得的函數(shù)值當(dāng)做元素的存儲(chǔ)位置,在結(jié)構(gòu)中按此位置取元素比較,若關(guān)鍵碼相等,則搜索成功。
1.2 哈希函數(shù)
哈希函數(shù)設(shè)置為 hash(key) = key % capacity,capacity是指存儲(chǔ)元素底層空間總的大小。
1.3 沖突

不同關(guān)鍵字通過(guò)相同哈希函數(shù)計(jì)算出相同的哈希地址,該種現(xiàn)象稱為哈希沖突或哈希碰撞。 把具有不同關(guān)鍵碼而具有相同哈希地址的數(shù)據(jù)元素稱為 “同義詞”。
1.4 避免沖突
1、設(shè)計(jì)哈希函數(shù);
2、調(diào)節(jié)負(fù)載因子。
散列表的負(fù)載因子(載荷因子)定義為:ɑ = 填入表中的元素個(gè)數(shù) / 哈希表的長(zhǎng)度。
ɑ 越大,表明填入表中的元素越多,產(chǎn)生沖突的可能性越大;而 ɑ 與哈希表長(zhǎng)度成反比,因此當(dāng)逼近或等于負(fù)載因子時(shí)需立即擴(kuò)容降低負(fù)載因子。【Java的系統(tǒng)庫(kù)限制負(fù)載因子為 0.75?!?/span>
1.5 解決沖突
1、 閉散列 / 開(kāi)放定址法
當(dāng)發(fā)生哈希沖突時(shí),如果哈希表未被裝滿,說(shuō)明在哈希表中必然還有空位置,那么可以 把key存放到?jīng)_突位置中的“下一個(gè)” 空位置中去。而尋找下一個(gè)空位置的方法如下:
① 線性探測(cè):從發(fā)生沖突的位置開(kāi)始,依次向后探測(cè),直到尋找到下一個(gè)空位置為止。缺點(diǎn):不能隨便物理刪除哈希表中已有的元素,若直接刪除元素會(huì)影響其他元素的搜索。比如刪除元素4,如果直接刪除掉,14查找起來(lái)可能會(huì)受影響。因此線性探測(cè)采用標(biāo)記的偽刪除法來(lái)刪除一個(gè)元素。

② 二次探測(cè):線性探測(cè)的缺陷是產(chǎn)生沖突的數(shù)據(jù)堆積在一塊,這與其找下一個(gè)空位置有關(guān)系,因?yàn)檎铱瘴恢玫姆绞骄褪前ぶ笾饌€(gè)去找,因此二次探測(cè)為了避免該問(wèn)題,找下一個(gè)空位置的方法為

或

其中,i = 1,2...,H?是通過(guò)散列函數(shù)對(duì)關(guān)鍵碼 key 進(jìn)行運(yùn)算得到的位置,m 是表的大小。

研究表明:當(dāng)表的長(zhǎng)度為質(zhì)數(shù)且表負(fù)載因子 ɑ 不超過(guò) 0.5 時(shí),新的表項(xiàng)一定能夠插入,而且任何一個(gè)位置都不會(huì)被探查兩次。因此只要表中有一半的空位置,就不會(huì)存在表滿的問(wèn)題。
因此,閉散列最大的缺陷就是空間利用率比較低,這也是哈希的缺陷。(消耗空間節(jié)省時(shí)間)
2、開(kāi)散列 / 哈希桶 / 鏈地址法 / 開(kāi)鏈法
首先對(duì)關(guān)鍵碼集合用散列函數(shù)計(jì)算散列地址,具有相同地址的關(guān)鍵碼歸于同一子集合,每一個(gè)子集合稱為一個(gè)桶,各個(gè)桶中的元素通過(guò)一個(gè)單鏈表鏈接起來(lái),各鏈表的頭結(jié)點(diǎn)存儲(chǔ)在哈希表中。簡(jiǎn)單來(lái)說(shuō)就算 數(shù)組+鏈表 的形式。

1.6 沖突嚴(yán)重時(shí)
剛才我們提到了,哈希桶其實(shí)可以看作將大集合的搜索問(wèn)題轉(zhuǎn)化為小集合的搜索問(wèn)題了,那如果沖突嚴(yán)重,就意味著小集合的搜索性能其實(shí)也時(shí)不佳的,這個(gè)時(shí)候我們就可以將這個(gè)所謂的小集合搜索問(wèn)題繼續(xù)進(jìn)行轉(zhuǎn)化,例如:
1、每個(gè)桶的背后是另一個(gè)哈希表;
2、每個(gè)桶的背后是一棵搜索樹(shù)。
1.7 tips
哈希函數(shù)作用是:建立元素與其存儲(chǔ)位置之前的對(duì)應(yīng)關(guān)系的,在存儲(chǔ)元素時(shí),先通過(guò)哈希函數(shù)計(jì)算元素在哈希表格中的存儲(chǔ)位置,然后存儲(chǔ)元素。好的哈希函數(shù)可以減少?zèng)_突的概率,但是不能夠絕對(duì)避免,萬(wàn)一發(fā)生哈希沖突,得需要借助哈希沖突處理方法來(lái)解決。
只要想哈希表格中存儲(chǔ)的元素超過(guò)兩個(gè),就有可能存在哈希沖突;
常見(jiàn)的哈希函數(shù)有:直接定址法、除留余數(shù)法、平方取中法、隨機(jī)數(shù)法、數(shù)字分析法、疊加法等;
常見(jiàn)哈希沖突處理:閉散列(線性探測(cè)、二次探測(cè))、開(kāi)散列(鏈地址法)、多次散列。
二、哈希表的實(shí)現(xiàn)
數(shù)組 + 鏈表(數(shù)組存放鏈表首節(jié)點(diǎn)地址,節(jié)點(diǎn)形成單鏈表)
準(zhǔn)備工作:
定義內(nèi)部類節(jié)點(diǎn) Node 、哈希表的成員變量:
public class HashBucket {
static class Node{
public int key;
public int val;
public Node next;
public Node(int key, int val){
this.key = key;
this.val = val;
}
}
public Node[] array = new Node[10];
public int usedSize;
}2.1 插入、擴(kuò)容
2.1.1 基本思想

// 默認(rèn)負(fù)載因子的限制值
public static final double DEFAULT_LOAD_FACTOR = 0.75;
public void push(int key, int val){
int index = key % array.length;
Node cur = array[index];
// 找到有無(wú)該節(jié)點(diǎn)
while(cur != null){
if (cur.key == key){
cur.val = val; // 更新 value值
return;
}
cur = cur.next;
}
// 沒(méi)有找到該節(jié)點(diǎn),那么插入
Node node = new Node(key,val);
node.next = array[index];
array[index] = node;
usedSize++;
if (doLoadFactor() >= DEFAULT_LOAD_FACTOR){
resize();
}
}
private double doLoadFactor(){ // 計(jì)算當(dāng)前的負(fù)載因子
return usedSize*1.0 / array.length; // 乘1.0的目的是將整型隱式轉(zhuǎn)換為浮點(diǎn)型
}
private void resize(){
// 錯(cuò)誤寫(xiě)法,擴(kuò)容不僅僅是將容器放大容量,還應(yīng)該重新哈希
//array = Arrays.copyOf(array,2*array.length);
Node[] newArray = new Node[2*array.length]; // 存放到新的數(shù)組
// 遍歷原來(lái)的數(shù)組,重新哈希
for (int i = 0; i < array.length; i++) {
Node cur = array[i];
while (cur != null) {
int newIndex = cur.key % newArray.length;
Node curNext = cur.next; // 記錄原先數(shù)組的下一個(gè)節(jié)點(diǎn)
cur.next = newArray[newIndex];
newArray[newIndex] = cur;
cur = curNext; // 回到原先數(shù)組的下一個(gè)節(jié)點(diǎn),而不是新數(shù)組的下一個(gè)節(jié)點(diǎn)
}
}
array = newArray;
}2.1.2 測(cè)試示例:
1、push()
public class Test {
public static void main(String[] args) {
HashBucket h = new HashBucket();
h.push(1,111);
h.push(2,222);
h.push(4,444);
h.push(14,141414);
h.push(21,212121);
h.push(12,121212);
h.push(24,242424);
}當(dāng)前存放元素個(gè)數(shù)為 7,負(fù)載因子為 0.7,未超過(guò) 0.75,不擴(kuò)容。測(cè)試結(jié)果:

2、擴(kuò)容之后

2.2 查找,獲取value值
// 查找有無(wú)key,與push方法中的查找類似
public int getVal(int key){
int index = key % array.length;
Node cur = array[index];
while (cur != null){
if (cur.key == key){
return cur.val;
}
cur = cur.next;
}
return -1;
}上述的代碼中 key 和 value 的類型都是整型,如果想要其他類型,就定義泛型結(jié)構(gòu):
package hash;
public class HashBucket2<K, V> {
static class Node<K,V>{
public K key;
public V val;
public Node<K, V> next;
public Node(K key, V val){
this.key = key;
this.val = val;
}
}
public Node<K, V>[] array = (Node<K, V>[])new Node[10]; // 強(qiáng)轉(zhuǎn)
public int usedSize;
public static final double DEFAULT_LOAD_FACTOR = 0.75;
public void push(K key, V val){
int hashcode = key.hashCode(); // 調(diào)用 hashCode,使引用類型轉(zhuǎn)換為整數(shù)類型
int index = hashcode % array.length;
Node<K,V> cur = array[index];
while(cur != null){
if (cur.key.equals(key)){ // 引用類型用 .equals()
cur.val = val;
return;
}
cur = cur.next;
}
Node<K, V> node = new Node<>(key,val);
node.next = array[index];
array[index] = node;
usedSize++;
if (doLoadFactor() >= DEFAULT_LOAD_FACTOR){
resize();
}
}
private double doLoadFactor() { // 計(jì)算當(dāng)前的負(fù)載因子
return usedSize * 1.0 / array.length;
}
private void resize(){
Node<K,V>[] newArray = (Node<K, V>[])new Node[2*array.length]; // 存放到新的數(shù)組
// 遍歷原來(lái)的數(shù)組,重新哈希
for (int i = 0; i < array.length; i++) {
Node<K,V> cur = array[i];
while (cur != null) {
int hashcode = cur.key.hashCode();
int newIndex = hashcode % newArray.length;
Node<K,V> curNext = cur.next; // 記錄原先數(shù)組的下一個(gè)節(jié)點(diǎn)
cur.next = newArray[newIndex];
newArray[newIndex] = cur;
cur = curNext; // 回到原先數(shù)組的下一個(gè)節(jié)點(diǎn),而不是新數(shù)組的下一個(gè)節(jié)點(diǎn)
}
}
array = newArray;
}
public V getVal(K key){
int hashcode = key.hashCode();
int index = hashcode % array.length;
Node<K,V> cur = array[index];
while(cur != null){
if (cur.key.equals(key)){
return cur.val;
}
cur = cur.next;
}
return null;
}
}測(cè)試:
package hash;
import java.util.Objects;
class Student{
public String id;
public Student(String id){
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("409");
Student student2 = new Student("409");
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
// 當(dāng)自定義類沒(méi)有重寫(xiě) equals() 和 hashCode() 方法時(shí),下面輸出false
System.out.println(student1.equals(student2));
HashBucket2<Student, String> h = new HashBucket2<>();
h.push(student1,"Li");
h.push(student2,"NAZA");
System.out.println(h.getVal(student1));
}
}輸出結(jié)果:
51548
51548
true
NAZA
(此處插入一個(gè)面試題:對(duì)于HashMap,如果一個(gè)對(duì)象為 key 時(shí),hashCode 和 equals 方法的用法要注意什么?)
2.3 性能分析
雖然哈希表一直在和沖突做斗爭(zhēng),但在實(shí)際使用過(guò)程中,我們認(rèn)為哈希表的沖突率是不高的,沖突個(gè)數(shù)是可控的,也就是每個(gè)桶中的鏈表的長(zhǎng)度是一個(gè)常數(shù),所以,通常意義下,我們認(rèn)為哈希表的插入/刪除/查找時(shí)間復(fù)雜度是 O(1) 。
2.4 和Java集合類的關(guān)系
1、HashMap 和 HashSet 即 java 中利用哈希表實(shí)現(xiàn)的 Map 和 Set
2、java 中使用的是哈希桶方式解決沖突的
3、java 會(huì)在沖突鏈表長(zhǎng)度大于一定閾值后,將鏈表轉(zhuǎn)變?yōu)樗阉鳂?shù)(紅黑樹(shù))
4、java 中計(jì)算哈希值實(shí)際上是調(diào)用類的 hashCode 方法,進(jìn)行 key 的相等性比較是調(diào)用 key 的 equals 方法。
所以如果要用自定義類作為 HashMap 的 key 或者 HashSet 的值,必須重寫(xiě) hashCode 和 equals 方法,而且要做到 equals 相等的對(duì)象,hashCode 產(chǎn)生的整數(shù)結(jié)果一定是一致的。但是 hashCode 一樣時(shí),equals 得到的結(jié)果可能一樣也可能不一樣,因?yàn)?hashCode 一樣只能說(shuō)明存放在表中的位置是一樣的,key 不一定一樣。
簡(jiǎn)而言之:如果兩個(gè)對(duì)象根據(jù) equals() 方法比較是相等的,那么調(diào)用這兩個(gè)對(duì)象的 hashCode() 方法必須產(chǎn)生相同的整數(shù)結(jié)果。反之,兩個(gè)對(duì)象的 hashCode() 相同,它們并不一定 equals()。(面試題解答的核心原則)
【用哈希時(shí)使用的自定義類型不需要實(shí)現(xiàn)可比較,但是用 TreeMap 或 TreeSet 時(shí)需要可比較的類型】
三、Map 和 Set 練習(xí)題
3.1 統(tǒng)計(jì)英文閱讀中的單詞詞頻
使用 Map 的核心邏輯:如果 map 中存在該單詞,則計(jì)數(shù)+1;否則放入 map 并設(shè)置計(jì)數(shù)為1。
package hash;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static Map<String, Integer> countWords(String[] str) {
Map<String, Integer> map = new HashMap<>();
for (String s : str) {
if (map.get(s) != null){
int val = map.get(s); // 取得當(dāng)前的value值
map.put(s,val+1);
}else{
map.put(s,1);
}
}
return map;
}
public static void main(String[] args) {
String[] str = {"Long","Long","Long","time","age","there","is","a","pig","live","in","a","house"};
Map<String, Integer> map = countWords(str);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> s : entrySet) {
System.out.println("word: "+s.getKey() + " nums: "+s.getValue());
}
}
}3.2 找出單身狗/只出現(xiàn)一次的數(shù)字
解法有多種,除了使用異或的方法,還能使用集合來(lái)解決。
使用集合的基本思想:遍歷數(shù)組,第一次出現(xiàn)的放到 Set 中,第二次出現(xiàn)的就將其移出,最后 Set 里面得到的就是只出現(xiàn)一次的數(shù)字。
public int singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for (int n : nums) {
if (set.contains(n)){
set.remove(n);
}else {
set.add(n);
}
}
for (int n : nums) {
if (set.contains(n)){
return n;
}
}
return -1;
}3.3復(fù)制帶隨機(jī)指針的鏈表
采用 Map 會(huì)非常方便,但是如果不使用 Map 該如何復(fù)制?

public Node copyRandomList(Node head) {
HashMap<Node, Node> map = new HashMap<>();
// 1、根據(jù)val值新建節(jié)點(diǎn)
Node cur = head;
while (cur != null){
Node node = new Node(cur.val);
map.put(cur,node); // 將舊節(jié)點(diǎn)地址和新節(jié)點(diǎn)地址一起放到map中
cur = cur.next;
}
// 2、重新遍歷,設(shè)置next和random
cur = head;
while (cur != null){
// get()返回的是val值,即返回的是存放在map中的 新地址
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head); // 返回的是頭節(jié)點(diǎn)的新地址
}3.4寶石與石頭
解法可以用數(shù)組也可以采用集合的方法。使用數(shù)組的方法執(zhí)行用時(shí)會(huì)比使用集合的方法用時(shí)短。
采用集合:將一顆顆“寶石”存放到 Set 中,遍歷“石頭”,查看“石頭”里面是否包含了“寶石”。
public int numJewelsInStones(String jewels, String stones) {
HashSet<Character> jewelsSet = new HashSet<>();
for(int i = 0; i < jewels.length(); i++){
char s = jewels.charAt(i);
jewelsSet.add(s);
}
int count = 0;
for(int i = 0; i < stones.length(); i++){
char s = stones.charAt(i);
if(jewelsSet.contains(s)){
count++;
}
}
return count;
}3.5壞鍵盤(pán)打字
基本思想:將實(shí)際輸出的字符存放到 Set 中,遍歷應(yīng)該輸入的字符串,注意有重復(fù)輸出的情況。

public static void findBrokenKey(String str1, String str2){
// 1、先全部轉(zhuǎn)化為大寫(xiě)
str1 = str1.toUpperCase();
str2 = str2.toUpperCase();
// 2、將實(shí)際輸出的按鍵存儲(chǔ)到 set 中
HashSet<Character> setAct = new HashSet<>();
for (int i = 0; i < str2.length(); i++) {
char c2 = str2.charAt(i);
setAct.add(c2);
}
// 3、遍歷應(yīng)該輸出的字符串
/*for (int i = 0; i < str1.length(); i++) {
char c1 = str1.charAt(i);
if (!setAct.contains(c1)) {
System.out.print(c1);
}
}*/ // 存在問(wèn)題:重復(fù)輸出
// 4、修正,避免重復(fù)輸出的情況
HashSet<Character> setBroken = new HashSet<>();
for (int i = 0; i < str1.length(); i++) {
char c1 = str1.charAt(i);
// 如果已經(jīng)存放到 setBroken ,即已經(jīng)被包含,就跳過(guò)if語(yǔ)句塊
if (!setAct.contains(c1) && !setBroken.contains(c1)) {
setBroken.add(c1);
System.out.print(c1);
}
}
}
public static void main(String[] args) {
String s1 = "7_This_is_a_test";
String s2 = "_hs_s_a_es";
findBrokenKey(s1,s2);
}3.6前K個(gè)高頻單詞
基本思想:類似 Top-K:統(tǒng)計(jì)所有單詞的詞頻,前K個(gè)建小根堆,比較交換重建小根堆……
不過(guò)題目要求說(shuō)“如果不同的單詞有相同出現(xiàn)頻率, 按字典順序 排序”,因此除了關(guān)注詞頻,還得關(guān)注單詞的大小比較,因此使用 Map 存放兩個(gè)值。
重點(diǎn)是建立 PriorityDeque 時(shí)傳入的比較器的方法重寫(xiě)。
public List<String> topKFrequent(String[] words, int k) {
// 1、統(tǒng)計(jì)單詞詞頻
HashMap<String, Integer> wordFrequency = new HashMap<>();
for (String word: words) {
if (wordFrequency.get(word) == null){
wordFrequency.put(word,1);
}else{
int val = wordFrequency.get(word);
wordFrequency.put(word,val+1);
}
}
// 2、建堆
PriorityQueue<Map.Entry<String, Integer>> minHeap = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if (o1.getValue().compareTo(o2.getValue()) == 0){ // 如果詞頻一樣,就按照字典順序比較
return o2.getKey().compareTo(o1.getKey()); // 此處應(yīng)該建大根堆,因?yàn)槿绻⑿「眩竺鎟everse之后與題目要求不符
}
return o1.getValue().compareTo(o2.getValue()); // 根據(jù)詞頻建堆
}
});
// 3、遍歷HashMap
for (Map.Entry<String,Integer> entry: wordFrequency.entrySet()) {
if (minHeap.size() < k) { // 前k個(gè)單詞建立小根堆
minHeap.offer(entry);
}else{
Map.Entry<String, Integer> top = minHeap.peek(); // 得到目前堆頂?shù)脑?
// 進(jìn)行比較
if (top.getValue().compareTo(entry.getValue()) < 0){ // 如果后面的元素詞頻大于前面最小的單詞的詞頻,就“交換”
minHeap.poll();
minHeap.add(entry);
}else if(top.getValue().compareTo(entry.getValue()) == 0){ // 如果字母詞頻相同
if (top.getKey().compareTo(entry.getKey()) > 0){ // 字母較大的,存放到堆里面
minHeap.poll();
minHeap.add(entry);
}
}
}
}
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < k; i++) {
Map.Entry<String, Integer> tmp = minHeap.poll();
list.add(tmp.getKey());
}
Collections.reverse(list); // ArrayList 繼承的Collections接口可以翻轉(zhuǎn)
return list;
}通過(guò)上面的練習(xí),我發(fā)現(xiàn)似乎涉及同一個(gè)元素出現(xiàn)次數(shù) / 統(tǒng)計(jì)頻率 / 有無(wú)某元素 / 復(fù)制等類型都能使用到 Map 和 Set ,因此在處理超過(guò)2個(gè)以上的數(shù)據(jù)時(shí),就得應(yīng)用集合的知識(shí),發(fā)散性思考。
總結(jié)
到此這篇關(guān)于Java哈希表HashTable(又稱散列表)實(shí)現(xiàn)及練習(xí)題的文章就介紹到這了,更多相關(guān)Java哈希表HashTable內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud中的Ribbon負(fù)載均衡器詳細(xì)解析
這篇文章主要介紹了SpringCloud中的Ribbon負(fù)載均衡器詳細(xì)解析,Ribbon 是一個(gè)基于 HTTP 和 TCP 的客戶端負(fù)載均衡工具,它基于 Netflix Ribbon 實(shí)現(xiàn),通過(guò)封裝可以讓我們輕松地將面向服務(wù)的 REST 模版請(qǐng)求自動(dòng)轉(zhuǎn)換成客戶端負(fù)載均衡的服務(wù)調(diào)用,需要的朋友可以參考下2024-01-01
Java類型通配符應(yīng)用實(shí)戰(zhàn)分析
這篇文章主要介紹了Java類型通配符應(yīng)用實(shí)戰(zhàn),簡(jiǎn)單分析了Java類型通配符概念、原理并結(jié)合實(shí)例形式給出了Java類型通配符相關(guān)使用技巧,需要的朋友可以參考下2019-07-07
如何把Spring Cloud Data Flow部署在Kubernetes上
這篇文章主要介紹了把Spring Cloud Data Flow部署在Kubernetes上,再跑個(gè)任務(wù)試試,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Spring運(yùn)行環(huán)境Environment的解析
本文主要介紹了Spring運(yùn)行環(huán)境Environment的解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
java 二進(jìn)制數(shù)據(jù)與16進(jìn)制字符串相互轉(zhuǎn)化方法
今天小編就為大家分享一篇java 二進(jìn)制數(shù)據(jù)與16進(jìn)制字符串相互轉(zhuǎn)化方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Java實(shí)現(xiàn)文件批量重命名具體實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)文件批量重命名具體實(shí)例,需要的朋友可以參考下2014-02-02
SpringBoot實(shí)現(xiàn)虛擬線程的方案
Java19引入虛擬線程,本文就來(lái)介紹一下SpringBoot實(shí)現(xiàn)虛擬線程的方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08

