当前位置: 首页 > 编程日记 > 正文

Redis源码和java jdk源码中hashcode的不同实现

一.redis实际上是使用了siphash

这个比较简单,我说的简单是指redis代码比较少不像jdk一样调用C++代码调用栈非常深。

先看这个rehashing.c

主要就是dictKeyHash函数,需要调用dict.h头文件中定义的dictGenHashFunction

#include "redis.h"
#include "dict.h"void _redisAssert(char *x, char *y, int l) {printf("ASSERT: %s %s %d\n",x,y,l);exit(1);
}unsigned int dictKeyHash(const void *keyp) {unsigned long key = (unsigned long)keyp;key = dictGenHashFunction(&key,sizeof(key));key += ~(key << 15);key ^=  (key >> 10);key +=  (key << 3);key ^=  (key >> 6);key += ~(key << 11);key ^=  (key >> 16);return key;
}int dictKeyCompare(void *privdata, const void *key1, const void *key2) {unsigned long k1 = (unsigned long)key1;unsigned long k2 = (unsigned long)key2;return k1 == k2;
}dictType dictTypeTest = {dictKeyHash,                   /* hash function */NULL,                          /* key dup */NULL,                          /* val dup */dictKeyCompare,                /* key compare */NULL,                          /* key destructor */NULL                           /* val destructor */
};

dict.h

uint64_t dictGenHashFunction(const void *key, int len);
uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len);
void dictEmpty(dict *d, void(callback)(void*));
void dictEnableResize(void);
void dictDisableResize(void);
int dictRehash(dict *d, int n);
int dictRehashMilliseconds(dict *d, int ms);
void dictSetHashFunctionSeed(uint8_t *seed);
uint8_t *dictGetHashFunctionSeed(void);
unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanBucketFunction *bucketfn, void *privdata);
uint64_t dictGetHash(dict *d, const void *key);
dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash);

代码实现是在dict.c

注释已经说明了是实现在siphash.c

/* The default hashing function uses SipHash implementation* in siphash.c. */uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k);
uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k);uint64_t dictGenHashFunction(const void *key, int len) {return siphash(key,len,dict_hash_function_seed);
}uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) {return siphash_nocase(buf,len,dict_hash_function_seed);
}

其实这个siphash.c是第三方实现的github上有源码,这里只应用作者的说明就行了:

/*SipHash reference C implementationCopyright (c) 2012-2016 Jean-Philippe Aumasson<jeanphilippe.aumasson@gmail.com>Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>To the extent possible under law, the author(s) have dedicated all copyrightand related and neighboring rights to this software to the public domainworldwide. This software is distributed without any warranty.You should have received a copy of the CC0 Public Domain Dedication alongwith this software. If not, see<http://creativecommons.org/publicdomain/zero/1.0/>.----------------------------------------------------------------------------This version was modified by Salvatore Sanfilippo <antirez@gmail.com>in the following ways:1. We use SipHash 1-2. This is not believed to be as strong as thesuggested 2-4 variant, but AFAIK there are not trivial attacksagainst this reduced-rounds version, and it runs at the same speedas Murmurhash2 that we used previously, why the 2-4 variant sloweddown Redis by a 4% figure more or less.2. Hard-code rounds in the hope the compiler can optimize it morein this raw from. Anyway we always want the standard 2-4 variant.3. Modify the prototype and implementation so that the function directlyreturns an uint64_t value, the hash itself, instead of receiving anoutput buffer. This also means that the output size is set to 8 bytesand the 16 bytes output code handling was removed.4. Provide a case insensitive variant to be used when hashing strings thatmust be considered identical by the hash table regardless of the case.If we don't have directly a case insensitive hash function, we need toperform a text transformation in some temporary buffer, which is costly.5. Remove debugging code.6. Modified the original test.c file to be a stand-alone function testingthe function in the new form (returing an uint64_t) using just therelevant test vector.*/

作者官网:https://131002.net/siphash/

源代码:https://github.com/veorq/SipHash

SipHash:快速短输入PRF

下载  |  攻击  |  用户  |  CRYPTANALYSIS  |  第三方实施

SipHash是一系列伪随机函数(也称为键控散列函数),针对短消息的速度进行了优化。 

目标应用程序包括网络流量身份验证和 防止散列泛滥 DoS攻击。 

SipHash 安全,快速,简单(真实):

  • SipHash比以前的加密算法更简单,更快(例如基于通用哈希的MAC)
  • SipHash在性能上与不安全的 非加密算法竞争(例如MurmurHash)

我们建议哈希表切换到SipHash作为哈希函数。 SipHash的用户已经包括FreeBSD,OpenDNS,Perl 5,Ruby或Rust。 

原始SipHash返回64位字符串。随后根据用户的需求创建了返回128位字符串的版本。 

知识产权: 我们不了解与SipHash相关的任何专利或专利申请,我们也不打算申请任何专利。SipHash 的参考代码是在CC0许可下发布的,这是一种类似公共领域的许可。 

SipHash的设计者是

  • Jean-Philippe Aumasson(瑞士Kudelski Security)
  • Daniel J. Bernstein(美国伊利诺伊大学芝加哥分校)

联系方式:jeanphilippe.aumasson@gmail.com   djb@cr.yp.to

下载

  • 研究论文 “SipHash:快速短期投入PRF”(在DIAC研讨会和INDOCRYPT 2012上 接受发表)
  • 2012年INDOCRYPT(伯恩斯坦)SipHash演讲的 幻灯片
  • 在DIAC(Aumasson)展示SipHash的 幻灯片
  • 参考C实现。

=============================================================

二.java的实现比较复杂

又要分字符串的hashCode()和object的hashCode()

1.字符串的hashCode()

    /*** Returns a hash code for this string. The hash code for a* {@code String} object is computed as* <blockquote><pre>* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]* </pre></blockquote>* using {@code int} arithmetic, where {@code s[i]} is the* <i>i</i>th character of the string, {@code n} is the length of* the string, and {@code ^} indicates exponentiation.* (The hash value of the empty string is zero.)** @return  a hash code value for this object.*/public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}

需要注意的是为什么 String hashCode 方法选择数字31作为乘子,可以看看这篇帖子,这个属于数学问题了。

原因就是

第一,31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。另外一些相近的质数,比如37、41、43等等,也都是不错的选择。

第二、31可以被 JVM 优化,31 * i = (i << 5) - i

Stack Overflow 上关于这个问题的讨论,Why does Java's hashCode() in String use 31 as a multiplier?。其中排名第一的答案引用了《Effective Java》中的一段话,这里也引用一下:

选择值31是因为它是奇数素数。 如果它是偶数并且乘法溢出,则信息将丢失,因为乘以2相当于移位。
使用素数的优势不太明显,但它是传统的。 
31的一个很好的特性是乘法可以用移位和减法代替以获得更好的性能:
31 * i ==(i << 5) -  i`。 现代VM自动执行此类优化。

其他解释:

正如 Goodrich 和 Tamassia 指出的那样,如果你对超过 50,000 个英文单词
(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,
并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个,
所以在上面几个常数中,常数 31 被 Java 实现所选用也就不足为奇了。

这个问题到底完结。

------------------------------------

2.jdk1.8 Object的hashCode()

完整的流程:

此图出自:《hotspot中java对象默认hashcode的生成方式 》

先看hashmap算key的hashCode源码

大量使用hash函数

翻译如下:

/ * ----------------静态实用程序-------------- * /
  计算key.hashCode()并将散列(XOR)更高的散列位降低。
因为该Table使用2次幂掩蔽,所以仅在当前掩码之上的位中变化的散列组将始终发生冲突。 (在已知的例子中是一组Float键,在小表中保存连续的整数。)
因此,我们应用一种向下传播较高位的影响的变换。
在速度,效用和比特扩展质量之间存在权衡。 因为许多常见的哈希集合已经合理分布(因此不会受益于传播),并且因为我们使用树来处理容器中的大量冲突,所以我们只是以最简易的方式对一些移位的位进行异或,以减少系统损失, 以及由于Table边界而包含最高位的影响,否则这些位将永远不会用于索引计算。

直接是native的实现了

如何在jvm源码中定位到某个Java本地方法对应的本地方法源码

比如说java.lang.Object#hashCode(),如何在jvm源码定位它?

从 jdk/src/share/native/java/lang/Object.c 文件里, 你可以找到

    {"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.cpp中定义了JVM_IHashCode()函数, 他又调用ObjectSynchronizer::FastHashCode;

FastHashCode在 synchronizer.cpp, FastHashCode调用get_next_hash()。

真正的计算hashcode的代码在 synchronizer.cpp的get_next_hash()。

jvm.cpp

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

synchronizer.cpp 

intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {if (UseBiasedLocking) {// NOTE: many places throughout the JVM do not expect a safepoint// to be taken here, in particular most operations on perm gen// objects. However, we only ever bias Java instances and all of// the call sites of identity_hash that might revoke biases have// been checked to make sure they can handle a safepoint. The// added check of the bias pattern is to avoid useless calls to// thread-local storage.if (obj->mark()->has_bias_pattern()) {// Box and unbox the raw reference just in case we cause a STW safepoint.Handle hobj (Self, obj) ;// Relaxing assertion for bug 6320749.assert (Universe::verify_in_progress() ||!SafepointSynchronize::is_at_safepoint(),"biases should not be seen by VM thread here");BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());obj = hobj() ;assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");}}// hashCode() is a heap mutator ...// Relaxing assertion for bug 6320749.assert (Universe::verify_in_progress() ||!SafepointSynchronize::is_at_safepoint(), "invariant") ;assert (Universe::verify_in_progress() ||Self->is_Java_thread() , "invariant") ;assert (Universe::verify_in_progress() ||((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant") ;ObjectMonitor* monitor = NULL;markOop temp, test;intptr_t hash;markOop mark = ReadStableMark (obj);// object should remain ineligible for biased lockingassert (!mark->has_bias_pattern(), "invariant") ;if (mark->is_neutral()) {hash = mark->hash();              // this is a normal headerif (hash) {                       // if it has hash, just return itreturn hash;}hash = get_next_hash(Self, obj);  // allocate a new hash codetemp = mark->copy_set_hash(hash); // merge the hash code into header// use (machine word version) atomic operation to install the hashtest = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);if (test == mark) {return hash;}// If atomic operation failed, we must inflate the header// into heavy weight monitor. We could add more code here// for fast path, but it does not worth the complexity.} else if (mark->has_monitor()) {monitor = mark->monitor();temp = monitor->header();assert (temp->is_neutral(), "invariant") ;hash = temp->hash();if (hash) {return hash;}// Skip to the following code to reduce code size} else if (Self->is_lock_owned((address)mark->locker())) {temp = mark->displaced_mark_helper(); // this is a lightweight monitor ownedassert (temp->is_neutral(), "invariant") ;hash = temp->hash();              // by current thread, check if the displacedif (hash) {                       // header contains hash codereturn hash;}// WARNING://   The displaced header is strictly immutable.// It can NOT be changed in ANY cases. So we have// to inflate the header into heavyweight monitor// even the current thread owns the lock. The reason// is the BasicLock (stack slot) will be asynchronously// read by other threads during the inflate() function.// Any change to stack may not propagate to other threads// correctly.}// Inflate the monitor to set hash codemonitor = ObjectSynchronizer::inflate(Self, obj);// Load displaced header and check it has hash codemark = 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 headerassert (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 codehash = test->hash();assert (test->is_neutral(), "invariant") ;assert (hash != 0, "Trivial unexpected object/monitor header usage.");}}// We finally get the hashreturn hash;
}
// hashCode() generation :
//
// Possibilities:
// * MD5Digest of {obj,stwRandom}
// * CRC32 of {obj,stwRandom} or any linear-feedback shift register function.
// * A DES- or AES-style SBox[] mechanism
// * One of the Phi-based schemes, such as:
//   2654435761 = 2^32 * Phi (golden ratio)
//   HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ;
// * A variation of Marsaglia's shift-xor RNG scheme.
// * (obj ^ stwRandom) is appealing, but can result
//   in undesirable regularity in the hashCode values of adjacent objects
//   (objects allocated back-to-back, in particular).  This could potentially
//   result in hashtable collisions and reduced hashtable efficiency.
//   There are simple ways to "diffuse" the middle address bits over the
//   generated hashCode values:
//static inline intptr_t get_next_hash(Thread * Self, oop obj) {intptr_t value = 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 = os::random() ;} elseif (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<intptr_t>(obj) >> 3 ;value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;} elseif (hashCode == 2) {value = 1 ;            // for sensitivity testing} elseif (hashCode == 3) {value = ++GVars.hcSequence ;} elseif (hashCode == 4) {value = cast_from_oop<intptr_t>(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 = v ;}value &= markOopDesc::hash_mask;if (value == 0) value = 0xBAD ;assert (value != markOopDesc::no_hash, "invariant") ;TEVENT (hashCode: GENERATE) ;return value;
}

翻译下:

hashCode()生成:

 可能性:
  * {obj,stwRandom}的MD5Digest
  * {obj,stwRandom}的CRC32或任何线性反馈移位寄存器功能。
  * DES或AES风格的SBox []机制
  *基于Phi的方案之一,例如:
    
2654435761 = 2^32 * Phi (golden ratio)
    HashCodeValue =((uintptr_t(obj)>> 3)* 2654435761)^ GVars.stwRandom;
  * Marsaglia的shift-xor RNG方案的变体。
*(obj ^ stwRandom)很有吸引力,但可能导致相邻对象(特别是背靠背分配的对象)的hashCode值出现不合需要的规律性。 这可能会导致哈希表冲突并降低哈希表效率。
有一些简单的方法可以在生成的hashCode值上“扩散”中间地址位。

该函数提供了基于某个hashCode 变量值的六种方法。怎么生成最终值取决于hashCode这个变量值。

0 - 使用Park-Miller伪随机数生成器(跟地址无关)
1 - 使用地址与一个随机数做异或(地址是输入因素的一部分)
2 - 总是返回常量1作为所有对象的identity hash code(跟地址无关)
3 - 使用全局的递增数列(跟地址无关)
4 - 使用对象地址的“当前”地址来作为它的identity hash code(就是当前地址)
5 - 使用线程局部状态来实现Marsaglia's xor-shift随机数生成(跟地址无关)

Xorshift随机数生成器是George Marsaglia发现的一类伪随机数生成器:

VM到底用的是哪种方法?

JDK 8 和 JDK 9 默认值:


JDK 8 以前默认值:是传0

虽然方式不一样,但有个共同点:java生成的hashCode和对象内存地址没什么关系。
HotSpot提供了一个VM参数来让用户选择identity hash code的生成方式:

#-XX:hashCode

参考:https://zhuanlan.zhihu.com/p/28270828

    public static void main(String[] args) {int[] arr0 = new int[3];int[] arr1 = new int[3];//arr0.hashCode(); // 触发arr0计算identity hash code//arr1.hashCode(); // 触发arr1计算identity hash codeSystem.out.println(arr0);System.out.println(arr1);}

实验:

交互arr0和1

两次输出一样的地址,加上hashCode()就和顺序有关了:

原因是:

对象的hashcode并不是在创建对象时就计算好的,而是在第一次使用的时候,也就是首次调用hashCode方法时进行计算,并存储在对象的标记字中的。

在VM里,Java对象会在首次真正使用到它的identity hash code(例如通过Object.hashCode() / System.identityHashCode())时调用VM里的函数来计算出值,然后会保存在对象里,后面对同一对象查询其identity hash code时总是会返回最初记录的值。
不是在对象创建时计算的。

这组实现代码在HotSpot VM里自从JDK6的早期开发版开始就没变过,只是hashCode选项的默认值变了而已。

上面的程序在执行到这个 hashCode() 调用时,VM看到对象之前还没计算 identity hash code,才去计算并记录它。

这样的话,先 println(arr1) 就会使得 arr0 所引用的数组对象先被计算 identity hash code,在VM上就是从伪随机数列中取出某一项,然后再 println(arr2) 就会计算并记录 arr2 所引用的数组对象的 hash code,也就是取出那个伪随机数列的下一项。反之亦然。

所以无论先 println(arr1) 还是先 println(arr2) ,看到的都是 VM用来实现 identity hash code 的伪随机数列的某个位置的相邻两项,自然怎么交换都会看到一样的结果。

而如果不调用hash code自然就会触发identity hash code,所以交换顺序就没用...

这篇帖子也写得很好可以看看,作者对jvm是有一些深入的研究的:《How does the default hashCode() work?》

--------------

《Java Challengers #4: Comparing Java objects with equals() and hashcode()》

《Java Challengers #2: String comparisons How String methods, keywords, and operators process comparisons in the String pool》

先看源码

Object.java的equals:

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

String.java中的equals:

使用String类的Equals方法

equals()方法用于验证两个Java类的状态是否相同。因为equals()来自Object类,所以每个Java类都继承它。但equals()必须重写该方法才能使其正常工作。当然,String覆盖equals()

关于字符串要记住什么

  • Strings是不可变的,所以String不能改变状态。
  • 为了节省内存,JVM将Strings 保留在常量池中。String创建new时,JVM会检查其值并将其指向现有对象。如果常量池中没有该值,则JVM会创建一个新值String
  • 使用==运算符比较对象引用。使用该equals()方法比较的值String。相同的规则将应用于所有对象。
  • 使用new运算符时,即使存在具有相同值的值,String也会在String池中创建新的运算符String

-------------------------

下面都是Object的equals

equals()和hashcode()的常见错误

  • 忘记hashcode()equals()方法一起覆盖,反之亦然
  • 不覆盖equals()hashcode()使用哈希集合时HashSet
  • 返回方法中的常量值,hashcode()而不是返回每个对象的唯一代码。
  • 使用==equals互换。的==比较Object参考,而equals()比较对象值。

关于equals()和hashcode()要记住什么

  • 在POJO中始终覆盖equals()hashcode()方法是一种很好的做法。
  • 使用有效算法生成唯一的哈希码。
  • 覆盖equals()方法时,也始终覆盖该hashcode()方法。
  • equals()方法应该比较整个对象的状态:来自字段的值。
  • hashcode()方法可以是POJO的ID。
  • 当比较两个对象的哈希码的结果为假时,该equals()方法也应该为假。
  • 如果equals()hashcode()使用哈希集合时没有被重载,集合会有重复的元素。

使用equals()和hashcode()的准则

您应该只equals()为具有相同唯一哈希码ID的对象执行方法。当哈希码ID 同时,不应执行equals()

表1.哈希码比较

如果hashcode()比较......然后 …
返回true执行 equals()
返回false不要执行 equals()

出于性能原因,该原则主要用于SetHash收集。

对象比较规则

hashcode()比较返回false,该equals()方法也必须返回false。如果哈希码不同,则对象肯定不相等。

表2.与hashcode()的对象比较

当哈希码比较返回时......equals()方法应该返回...
真正对或错

equals()方法返回true,这意味着该对象相等的所有的值和属性。在这种情况下,哈希码比较也必须为真。

表3.与equals()的对象比较

equals()方法返回时......hashcode()方法应该返回...
真正真正
对或错

总结:

==永远是比较地址;new出来的两个对象地址自然也是不相等的;equals默认比较地址也就是和==等效,如果是字符串是比较内容而不是地址。如果重写了equals需要同步重写 hashCode()

相关文章:

android 7.0 短信监控,Android 7.0 监听网络变化的示例代码

Android7.0前&#xff0c;Android系统前网络切换时&#xff0c;会发广播&#xff0c;业务只要监听广播即可。public class NetChangeReceiver extends BroadcastReceiver {private static final String ANDROID_NET_CHANGE_ACTION "android.net.conn.CONNECTIVITY_CHANGE…

Mysql列类型-数值型

2019独角兽企业重金招聘Python工程师标准>>> 一、整数型&#xff1a; 1、取值范围&#xff1a; tinyint smallint mediumint int bigint 分别占用1、2、3、4、8个字节的存储空间 如&#xff1a;tinyint占用1个字节空间&#xff0c;它的取值范围&…

2018.10.22-dtoi1443奶牛逃亡(cowrun)

题目描述&#xff1a; Farmer John忘记修复他农场篱笆上的一个大洞&#xff0c;以至于篱笆围着的N&#xff08;1< N <1,000&#xff09;只奶牛从大洞中逃脱出来&#xff0c;并在农场里横冲直撞。每头在篱笆外的奶牛每分钟都将给他带来一美元的损失。FJ必须遍及每头奶牛、…

[转载]Linux 线程实现机制分析

自从多线程编程的概念出现在 Linux 中以来&#xff0c;Linux 多线应用的发展总是与两个问题脱不开干系&#xff1a;兼容性、效率。本文从线程模型入手&#xff0c;通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足&#xff0c;描述了 Linux 社区是如何看待…

Java12和Jdk12安装以及OpenJdk12源码

文档&#xff1a; JDK 12文档:https://docs.oracle.com/en/java/javase/12/ 下载&#xff1a; OracleJDK12下载&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk12-downloads-5295953.html csdn上我下好的&#xff0c;速度较快&#xff1a;https…

android 获取视频大小,Android 获取视频缩略图(获取视频每帧数据)的优化方案

速度对比左边的图片是通过方式1右边的图片是通过方式2speed.gif速度优化&#xff0c;效果拔群。在缩小2倍的Bitmap输出情况下使用MediaMetadataRetriever 抽帧的速度&#xff0c;每帧稳定在 300ms左右。使用MediaCodecImageReader 第一次抽帧。大概是200ms ,后续每帧则是50ms左…

Msql的DML、DDL、DCL的区别

DML(data manipulation language)&#xff1a;它们是SELECT、UPDATE、INSERT、DELETE&#xff0c;这4条命令是用来对数据库里的数据进行操作的语言 DDL(data definition language)&#xff1a;主要的命令有CREATE、ALTER、DROP等&#xff0c;DDL主要是用在定义或改变表(TABLE)的…

JSR 133 Java内存模型以及并发编程的最权威论文汇总

Java内存模型 先看官方文档&#xff1a; https://docs.oracle.com/javase/specs/ JSR 133&#xff1a;Java TM内存模型和线程规范修订版&#xff1a;https://www.jcp.org/en/jsr/detail?id133 JSR&#xff1a;Java规范请求所有JSR的列表&#xff1a;https://jcp.org/en/jsr/…

ajax实现自动刷新页面实例

html部分&#xff1a;<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>ajax实现自动刷新</title> </head> <body onLoad"Autofresh()"> <p>现在的时间是&#xff1a…

android aliasactivity作用,android activity-alias 的作用

activity-alias是android里为了重复使用Activity而设计的。当在Activity的onCreate()方法里&#xff0c;执行getIntent().getComponent().getClassName();得到的可能不是这个Activity的名字&#xff0c;有可能是别名的名字&#xff0c;例如&#xff1a;在AndroidMenifest.xml有…

1024 程序员节 | 请对身边的程序猿好一点

程序员节起源程序员的工作我们都知道&#xff0c;编程嘛。但为什么程序员节要在1024呢&#xff1f;1024最早火起来是因为一个“不可描述”的论坛&#xff0c;那里的回帖机制是&#xff1a;新用户发过贴之后&#xff0c;过1024秒才能发一贴&#xff0c;如果没到1024秒就又发了一…

stackoverflow上一个最会举例子的专家

https://stackoverflow.com/ Premraj是stackoverflow上一个一个最会举例子的专家&#xff0c;我特意收集了他的一些有趣的举例&#xff1a; Java弱引用最精彩的解释 https://stackoverflow.com/questions/299659/whats-the-difference-between-softreference-and-weakrefere…

Java中的两个关键字——super、this

Java中的两个关键字——super、this 神话丿小王子的博客主页 一、super super 是java中方的一个关键字&#xff0c;用它可以引用父类中的成员&#xff1a; super可用于访问父类中定义的属性 super可用于调用父类中定义的成员方法 super可用于在子类构造器中调用父类的构造器 使…

android system window,Android控件的fitSystemWindows属性

官方描述&#xff1a;根据系统窗体里的元素比如状态栏来调整View的布局。如果被设为true&#xff0c;控件的padding将会被调整为顶部留出一个statusBar的空间。类似于伪代码paddingTop"statusBarHeight"。重点说明&#xff1a;当布局内容可以延伸到状态栏&#xff0c…

Nestjs OpenAPI(Swagger)

官方文档 用来描述api 转载于:https://www.cnblogs.com/ajanuw/p/9846589.html

Jdk11,Jdk12的低延迟垃圾收集器ZGC

https://wiki.openjdk.java.net/display/zgc/Main Z垃圾收集器&#xff0c;也称为ZGC&#xff0c;是一种可扩展的低延迟垃圾收集器&#xff0c;旨在实现以下目标&#xff1a; 暂停时间不超过10毫秒暂停时间不会随堆或实时设置大小而增加处理堆范围从几百M到几T字节大小 一目了…

Android项目驱动式开发教程 第2版,《Android项目驱动式开发教程》第一章开发入门.ppt...

《Android项目驱动式开发教程》第一章开发入门1.4 项目框架分析 4 android:versionName"1.0" > 5 8 第9行代码android:icon用来声明整个APP的图标&#xff0c;图片一般都放在drawable文件夹下&#xff0c;使用资源引用的方式。 第10行代码android:label用来声明整…

getLocationInWindow getLocationOnScreen getLeft , getTop, getBottom,getRight

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 最近做项目时&#xff0c;发现在activity的onCreate()和onResume()方法里调用View.getLocationInWindow() 时&#xff0c;View.getLocationInWindow()返回空值&#xff0c;觉得很奇怪&#xff0c;因…

使用reflector对.NET反编译

reflector的下载地址&#xff1a;https://www.cr173.com/soft/355285.html 反编译后的结果&#xff1a; 转载于:https://www.cnblogs.com/ZaraNet/p/9848355.html

协程和Java实现

多线程的性能问题&#xff1a; 1.同步锁。 2.线程阻塞状态和可运行状态之间的切换。 3.线程上下文的切换。 协程&#xff0c;英文Coroutines&#xff0c;是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样&#xff0c;一个线程也可以拥有多个协程。 协程&a…

LoadRunner 技巧之 手动关联与预关联

上一节介绍了关联的原理与自动关联&#xff0c;除了自动关联还有另外两种关联方式&#xff1a;手动关联与 预关联。 手动关联 如果脚本很长&#xff0c;那么我们想找到一…

android app文档,android App项目需求描述文档.docx

本app是给外卖配送员用的&#xff0c;系统后台根据一定的逻辑生成或者建立运单&#xff0c;本App读到后台的运单讲外卖送到定外卖的手中本文档所需详细资料请到/s/1jGGgtLG下载与后台交互的地方不用实现&#xff0c;有数据显示的自己把交互函数写好 返回的测试数据写在交互函数…

如何利用微信小游戏的分包加载机制突破4M代码包体积限制

相信大家度过了一个不错的端午假期&#xff0c;在端午前夕&#xff0c;即6月15日晚上&#xff0c;微信小游戏宣布支持分包加载功能&#xff0c;白鹭引擎在端午节后第一天正式支持分包加载机制。在正式向开发者介绍如何使用前&#xff0c;我先为各位解读一下我对微信提供这个 AP…

一个会画图的工程师

发现小谢图画的很好&#xff0c;虽然有些也是他引用的&#xff0c;但是我觉得还是很好所以这里收集下。 【RocketMQ源码学习】2-Namesrv 3-Remoting模块 rocketmq-remoting 模块是 RocketMQ 中负责网络通信的模块&#xff0c;被其他所有需要网络通信的模块依赖。它是基于 Net…

2016百度实习编程题:括号序列

不知如何解决 1.感觉贪心或者动态规划&#xff0c;不知道如何解决 2.做过生成合法括号序列的题目&#xff0c;想到用DFS补成合法的括号&#xff0c;然而没有成功

html动画怎么隐藏,JQuery操作div隐藏和显示的4种动画

Jquery-Div动画显示body{font-family:"宋体";font-size:13px;color:#415973;}#ShowDiv{display:none;width:300px;height:100px;border:1px solid #ccc;border-right:2px solid #888;border-bottom:2px solid #888;background-color:#f9f9f9;text-align:center;paddi…

@芥末的糖----------《后端加密》

bcryptsession 生命周期 session 标识产生的时机和清除时机:&#xff08;权限验证&#xff09; 用户已经登录&#xff1a;这个唯一标识会在用户登录时产生&#xff0c;用户点击退出时或者关闭浏览器时清除。 …

Java中的ClassLoader和SPI机制

深入探讨 Java 类加载器 成富是著名的Java专家&#xff0c;在IBM技术网站发表很多Java好文&#xff0c;也有著作。 线程上下文类加载器 线程上下文类加载器&#xff08;context class loader&#xff09;是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextCl…

html在线缓存视频,javascript – 如何为HTML视频添加缓冲

给你的视频提供ID&#xff1a;用于缓冲&#xff1a;var vid document.getElementById("myVideoOne");alert("Start: " vid.buffered.start(0) " End: " vid.buffered.end(0));var vid document.getElementById("myVideoTwo");aler…

ComponentName(String pkg, String cls)

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/51279606 开发中因为改项目包名&#xff0c;用了全局替换&#xff0c;误操作把改构造函数的第二个参数只写了类名&#xff0c;不是完整的全路径…