主頁 > 後端開發 > 問題:兩個物件值相同(x.equals(y) == true),但是可能存在hashCode不同嗎?

問題:兩個物件值相同(x.equals(y) == true),但是可能存在hashCode不同嗎?

2021-10-29 06:07:45 後端開發

面試官的考察點

這道題仍然是考察JVM層面的基本知識,面試官認為,基本功扎實,才能寫出健壯性和穩定性很高的代碼,

涉及到的技術知識

(x.equals(y)==true),這段代碼,看起來非常簡單,但其實里面還是涉及了一些底層知識點的,首先我們基于equals這個方法進行探索,

equals這個方法,在每個物件中都存在,以String型別為例,其方法定義如下

public boolean equals(Object anObject) {
  if (this == anObject) { 
    return true;
  }
  if (anObject instanceof String) { //判斷物件實體是否是String
    String anotherString = (String)anObject; //強轉成string型別
    int n = value.length;
    if (n == anotherString.value.length) { //如果兩個字串相等,那么它們的長度自然相等,
      //遍歷兩個比較的字串,轉換為char型別逐個進行比較,
      char v1[] = value;  
      char v2[] = anotherString.value;
      int i = 0;
      while (n-- != 0) {
        if (v1[i] != v2[i]) //采用`==`進行判斷,如果不相同,則回傳false
          return false;
        i++;
      }
      return true; //否則回傳true,
    }
  }
  return false;
}

首先來分析第一段代碼,判斷傳遞進來的這個物件和當前物件實體this是否相等,如果相等則回傳true

  if (this == anObject) { 
    return true;
  }

==號的處理邏輯是怎么實作的呢?

了解==判斷

在java語言中==運算子號,這個比較大家都知道,是基于參考物件的比較,具體其實還有一些其他的區別,

JVM會根據==兩邊相互比較的操作型別不同,在編譯時生成不同的指令,

  1. 對于boolean,byte、short、int、long這種整形運算元,會生成if_icmpne指令,該指令用于比較整形數值是否相等,關于if_icmpne指令可以參見:Chapter 4. The class File Format,它在Hotspot VM中的bytecodeInterpreter原始碼中的具體實作如下

    #define COMPARISON_OP(name, comparison)                                      
    CASE(_if_icmp##name): {                                                
      int skip = (STACK_INT(-2) comparison STACK_INT(-1))                
        ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3;             
      address branch_pc = pc;                                            
      UPDATE_PC_AND_TOS(skip, -2);                                       
      DO_BACKEDGE_CHECKS(skip, branch_pc);                               
      CONTINUE;                                                          
    }
    

    可以看到實質是按照comparison運算式比較運算元堆疊中偏移量為-1和-2的兩個INT值,

  2. 如果運算元是物件的話,編譯器則會生成if_acmpne指令,與if_icmpne相比將i(int)改成了a(object reference),這個指令在JVM規范中的表述:Chapter 4. The class File Format,它在Hotspot VM中相應的實作可參考:

    COMPARISON_OP(name, comparison)                                        
      CASE(_if_acmp##name): {                                                
      int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1))          
        ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3;            
      address branch_pc = pc;                                            
      UPDATE_PC_AND_TOS(skip, -2);                                       
      DO_BACKEDGE_CHECKS(skip, branch_pc);                               
      CONTINUE;                                                          
    }
    

    STACK_OBJECT(-2) comparison STACK_OBJECT(-1)這一句即可看出比較的其實是運算元堆疊上兩個物件在堆中的指標,

對于JVM有一定了解得同學,必然知道((x==y)=true)這個判斷,如果xy的記憶體地址相同,那么意味著就是同一個物件,因此直接回傳true

因此從上面的分析中,得到的結論是,==判斷,比較的是兩個物件的記憶體地址,如果==回傳true,說明記憶體地址相同,

String.equals原始碼

繼續分析equals中的原始碼,剩余部分原始碼的實作邏輯是

  1. 比較兩個字串的長度是否相等,如果不相等,直接回傳false
  2. 把兩個String型別轉換為char[]陣列,并且按照陣列順序逐步比較每一個char字符,如果不相等,同樣回傳false
public boolean equals(Object anObject) {
  //省略
  if (anObject instanceof String) { //判斷物件實體是否是String
    String anotherString = (String)anObject; //強轉成string型別
    int n = value.length;
    if (n == anotherString.value.length) { //如果兩個字串相等,那么它們的長度自然相等,
      //遍歷兩個比較的字串,轉換為char型別逐個進行比較,
      char v1[] = value;  
      char v2[] = anotherString.value;
      int i = 0;
      while (n-- != 0) {
        if (v1[i] != v2[i]) //采用`==`進行判斷,如果不相同,則回傳false
          return false;
        i++;
      }
      return true; //否則回傳true,
    }
  }
  return false;
}

==和equals

通過上面的分析,我們知道,在 Java 中比較兩個物件是否相等主要是通過 ==號,比較的是他們在記憶體中的存放地址,Object 類是 Java 中的超類,是所有類默認繼承的,如果一個類沒有重寫 Object 的 equals方法,那么通過equals方法也可以判斷兩個物件是否相同,因為它內部就是通過==來實作的,

public boolean equals(Object obj) {
  return (this == obj);
}

這里的相同,是說比較的兩個物件是否是同一個物件,即在記憶體中的地址是否相等,而我們有時候需要比較兩個物件的內容是否相同,即類具有自己特有的“邏輯相等”概念,而不是想了解它們是否指向同一個物件,

例如比較如下兩個字串是否相同String a = "Hello"String b = new String("Hello"),這里的相同有兩種情形,是要比較 a 和 b 是否是同一個物件(記憶體地址是否相同),還是比較它們的內容是否相等?這個具體需要怎么區分呢?

如果使用 == 那么就是比較它們在記憶體中是否是同一個物件,但是 String 物件的默認父類也是 Object,所以默認的equals方法比較的也是記憶體地址,所以我們要重寫 equals方法,正如 String 原始碼中所寫的那樣,

  1. 先比較記憶體地址
  2. 再比較value值
public boolean equals(Object anObject) {
  //省略
  if (anObject instanceof String) { //判斷物件實體是否是String
    String anotherString = (String)anObject; //強轉成string型別
    int n = value.length;
    if (n == anotherString.value.length) { //如果兩個字串相等,那么它們的長度自然相等,
      //遍歷兩個比較的字串,轉換為char型別逐個進行比較,
      char v1[] = value;  
      char v2[] = anotherString.value;
      int i = 0;
      while (n-- != 0) {
        if (v1[i] != v2[i]) //采用`==`進行判斷,如果不相同,則回傳false
          return false;
        i++;
      }
      return true; //否則回傳true,
    }
  }
  return false;
}

這樣當我們 a == b時是判斷 a 和 b 是否是同一個物件,a.equals(b)則是比較 a 和 b 的內容是否相同,這應該很好理解,

JDK 中不止 String 類重寫了equals 方法,還有資料型別 Integer,Long,Double,Float等基本也都重寫了 equals 方法,所以我們在代碼中用 Long 或者 Integer 做業務引數的時候,如果要比較它們是否相等,記得需要使用 equals 方法,而不要使用 ==

因為使用 ==號會有意想不到的坑出現,像這種資料型別很多都會在內部封裝一個常量池,例如 IntegerCache,LongCache 等等,當資料值在某個范圍內時會直接從常量池中獲取而不會去新建物件,

如果要使用==,可以將這些資料包裝型別轉換為基本型別之后,再通過==來比較,因為基本型別通過==比較的是數值,但是在轉換的程序中需要注意 NPE(NullPointException)的發生,

了解Class中的HashCode

回過頭再看一下面試題: 兩個物件值相同(x.equals(y) == true),但是可能存在hash code不同嗎?

這個結果回傳true,假設xy是String型別,意味著它滿足兩個點,

  1. xy有可能是同一個記憶體地址,
  2. xy這兩個字串的值是相同的,

基于這兩個推斷,我們還沒辦法和hash code聯系上,也就是說,這兩個物件的參考地址相同,和hashCode是否有關系?

在Java中,任何一個物件都是派生自Object,而在Object中,有一個native方法hashCode()

public native int hashCode();

為什么要有hashCode?

對于包含容器結構的程式語言來說,基本上都會涉及到hashCode,它的主要作用是為了配合基于散列的集合一起作業,比如HashSet、HashTable、ConcurrentHashMap、HashMap等,

在這類的集合中添加元素時,首先需要判斷添加的元素是否存在(不允許存在重復元素),也許大多數人都會想到呼叫equals方法來逐個進行比較,這個方法確實可行,但是如果集合中已經存在一萬條資料或者更多的資料,如果采用equals方法去逐一遍歷每個元素的值進行比較,效率會非常低,

此時hashCode方法的作用就體現出來了,當集合要添加新的物件時,先呼叫這個物件的hashCode方法,得到對應的hashcode值,實際上在HashMap的具體實作中會用一個table保存已經存進去的物件的hashcode值:

  1. 如果table中沒有該hashcode值,它就可以直接存進去,不用再進行任何比較了;

  2. 如果存在該hashcode值, 就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址,所以這里存在一個沖突解決的問題,這樣一來實際呼叫equals方法的次數就大大降低了.

說通俗一點:Java中的hashCode方法就是根據一定的規則將與物件相關的資訊(比如物件的存盤地址,物件的欄位等)映射成一個數值,這個數值稱作為散列值,下面這段代碼是java.util.HashMap的中put方法的具體實作:

 public V put(K key, V value) {
   if (key == null)
     return putForNullKey(value);
   int hash = hash(key.hashCode());
   int i = indexFor(hash, table.length);
   for (Entry<K,V> e = table[i]; e != null; e = e.next) {
     Object k;
     if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
       V oldValue = https://www.cnblogs.com/mic112/p/e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
 
   modCount++;
   addEntry(hash, key, value, i);
   return null;
 }

所以通過hashCode,減少了查詢比較的次數,優化了查詢的效率同時也就減少了查詢的時間,

hashCode方法的實作

下面是hashCode這個方法的完整注釋說明,

 /**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
public native int hashCode();

從注釋的描述可以知道,hashCode 方法回傳該物件的哈希碼值,它可以為像 HashMap 這樣的哈希表有益,Object 類中定義的 hashCode 方法為不同的物件回傳不同的整形值,具有迷惑異議的地方就是
This is typically implemented by converting the internal address of the object into an integer
這一句,意為通常情況下實作的方式是將物件的內部地址轉換為整形值,

如果你不深究就會認為它回傳的就是物件的記憶體地址,我們可以繼續看看它的實作,但是因為這里是 native 方法所以我們沒辦法直接在這里看到內部是如何實作的,native 方法本身非 java 實作,如果想要看原始碼,只有下載完整的 jdk 原始碼,Oracle 的 JDK 是看不到的,OpenJDK 或其他開源 JRE 是可以找到對應的 C/C++ 代碼,我們在 OpenJDK 中找到 Object.c 檔案,可以看到hashCode 方法指向 JVM_IHashCode 方法來處理,

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JVM_IHashCode方法實作在 jvm.cpp中的定義為:

JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))  
  JVMWrapper("JVM_IHashCode");  
  // as implemented in the classic virtual machine; return 0 if object is NULL  
  return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;  
JVM_END 

這里是一個三目運算式,真正計算獲得 hashCode 值的是ObjectSynchronizer::FastHashCode,它具體的實作在synchronizer.cpp中,截取部分關鍵代碼片段,

intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {
  if (UseBiasedLocking) {
  
  //省略代碼片段
  
  // Inflate the monitor to set hash code
  monitor = ObjectSynchronizer::inflate(Self, obj);
  // Load displaced header and check it has hash code
  mark = monitor->header();
  assert (mark->is_neutral(), "invariant") ;
  hash = mark->hash();
  if (hash == 0) {
    hash = get_next_hash(Self, obj);
    temp = mark->copy_set_hash(hash); // merge hash code into header
    assert (temp->is_neutral(), "invariant") ;
    test = (markOop) Atomic::cmpxchg_ptr(temp, monitor, mark);
    if (test != mark) {
      // The only update to the header in the monitor (outside GC)
      // is install the hash code. If someone add new usage of
      // displaced header, please update this code
      hash = test->hash();
      assert (test->is_neutral(), "invariant") ;
      assert (hash != 0, "Trivial unexpected object/monitor header usage.");
    }
  }
  // We finally get the hash
  return hash;
}

從以上代碼片段中可以發現,實際計算hashCode的是 get_next_hash,該方法的部分代碼定義如下,

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = https://www.cnblogs.com/mic112/p/0 ;
  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = https://www.cnblogs.com/mic112/p/os::random() ;
  } else
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = cast_from_oop(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = cast_from_oop(obj) ;
  } else {
     // Marsaglia's xor-shift scheme with thread-specific state
     // This is probably the best overall implementation -- we'll
     // likely make this the default in future releases.
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = https://www.cnblogs.com/mic112/p/v ;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash,"invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}

get_next_hash的方法中我們可以看到,如果從0開始算的話,這里提供了6種計算hash值的方案,有自增序列,亂數,關聯記憶體地址等多種方式,其中官方默認的是最后一種,即亂數生成,可以看出hashCode也許和記憶體地址有關系,但不是直接代表記憶體地址的,具體需要看虛擬機版本和設定,

上面的整體描述還是比較復雜,直接說結論:

  1. 一個物件的hashCode,默認情況下如果沒有重寫,則由JVM中的get_next_hash方法來生成,這個生成的方式不一定和記憶體地址有關,默認是用亂數生成,兩個不同的物件,他們生成的hashCode可能會相同,如果存在這個問題,就是所謂的hash沖突,在HashMap中,解決hash沖突的方法是鏈式尋址法,

  2. 使用==這個運算式判斷,如果回傳true,意味著兩個物件的hashCode一定相同,

問題解答

問題:兩個物件值相同(x.equals(y) == true),但是可能存在hashCode不同嗎?

基于上面背景知識的梳理,再來回答這個問題,就有思路了,

理論情況下,x.equals(y)==true,如果沒有重寫equals這個方法,這兩個物件的記憶體地址是是相同的,也就意味著hashCode必然也相等,

那有沒有可能hashCode不同呢? 如果一定要做,也是可以實作的,我們來看下面這個例子,

public class App 
{
    public static void main( String[] args ) {
        A a = new A();
        B b = new B();
        System.out.println(a.equals(b));
        System.out.println(a.hashCode() + "," + b.hashCode());
    }
}
class A {
    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

class B {
}

運行結果如下

true
692404036,1554874502

從結果可以看到,equals回傳true,但是hashCode不同,

雖然我們模擬了這個可能性,但是原則上是錯誤的,因為這樣違反了hashCode的通用規定,可能會導致該類無法結合所有基于散列集合一起正常作業,比如HashMap、HashSet等,

public class App {
    public static void main( String[] args ) {
        Person p1=new Person("mic",18);
        Person p2=new Person("mic",18);

        HashMap<Person,String> hashMap=new HashMap<>();
        hashMap.put(p1,"mic");
        System.out.println(hashMap.get(p2));
    }
}
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

   //省略getter/setter

    @Override
    public boolean equals(Object obj) {
        if(this==obj){
            return true;
        }
        if(obj instanceof Person){
            if(this.getName()==((Person) obj).getName()&&this.getAge()==((Person) obj).getAge()){
                return true;
            }
        }
        return false;
    }
}

在上述代碼中,重寫了equals方法,但是沒有重寫hashCode方法,當呼叫Person類的hashCodo方法時,默認就是呼叫父類Object的hashCode方法,根據亂數回傳一個整數值,在equals方法中,我們是根據nameage進行判斷兩個物件是否相等,

main方法中構建了兩個物件p1p2,我們用HashMap存盤存盤,將物件作為key,把p1存入到hashMap中,再通過p2來獲取,在原則上,由于p1和p2相等,所以理論上是能夠拿到結果的,但是實際運行結果如下:

null

Process finished with exit code 0

熟知`HashMap 原理的同學應該知道,HashMap 是由陣列 + 鏈表的結構組成,這樣的結果就是因為它們 hashCode 不相等,所以放在了陣列的不同下標,當我們根據 Key 去查詢的時候結果就為 null,

得到的結果我們肯定不滿意,這里的 p1p2 雖然記憶體地址不同,但是它們的邏輯內容相同,我們認為它們應該是相同的,

為了避免這類問題的存在,所以約定了一條原則重寫equals方法的同時也需要重寫hashCode方法,這時一種通用約定,這個約定包含以下幾個方面,

  • 方法都必須始侄訓傳同一個值,
  • 如果兩個物件根據equals方法比較是相等的,那么呼叫這兩個物件中的hashCode方法都必須產生同樣的整數結果,
  • 如果兩個物件根據equals方法比較是不相等的,那么呼叫者兩個物件中的hashCode方法,則不一定要求hashCode方法必須產生不同的結果,但是給不相等的物件產生不同的整數散列值,是有可能提高散串列(hash table)的性能,

從理論上來說如果重寫了equals方法而沒有重寫hashCode方法則違背了上述約定的第二條,相等的物件必須擁有相等的散列值,但是規則是大家默契的約定,如果我們就喜歡不走尋常路,在重寫了 equals方法后沒有覆寫hashCode方法,就會造成嚴重的后果,

問題總結

綜合分析下來,對于該問題的正確解答如下

  1. 如果兩個物件值相同,有可能存在不同的hashCode,具體的實作方法是,只重寫equals方法,不重寫hashCode
  2. 這種處理方式會存在風險,在實際開發中,必須遵循重寫equals方法的同時也需要重寫hashCode方法這一原則,否則在Java散列集合類操作中,會存在null的問題,

關注[跟著Mic學架構]公眾號,獲取更多精品原創

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

標籤:Java

上一篇:activiti 根據 流程實體ID 獲取發起人

下一篇:Java 將Word保存為WPS和WPT格式

標籤雲
其他(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)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more