一.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()

Redis源码和java jdk源码中hashcode的不同实现相关推荐

  1. JAVA JDK 源码学习

    JAVA JDK 源码学习 ,以1.8为例,按照下面图片顺序依次学习: applet ,awt,beans,io,lang,math,net,nio,rmi,security,sql,text,tim ...

  2. java int类源码,一起学JDK源码 -- Integer类

    Integer类为java基本类型int的包装类,除了前面提到的Byte类,Short类中的大部分方法,Integer类中还提供了很多处理int类型的方法,接下来就让我们一起看看吧. 基础知识: 1. ...

  3. JAVA JDK源码在线阅读

    Java的版本是1.8.0_111,我把JDK源码发布到了github上,大家看起来也比较方便,地址: https://github.com/daiqingliang/java_jdk1.8.0_11 ...

  4. 【开源与项目实战:开源实战】77 | 开源实战一(下):通过剖析Java JDK源码学习灵活应用设计模式

    上一节课,我们讲解了工厂模式.建造者模式.装饰器模式.适配器模式在 Java JDK 中的应用,其中,Calendar 类用到了工厂模式和建造者模式,Collections 类用到了装饰器模式.适配器 ...

  5. 深深的码丨Java ArrayList 源码透析

    ArrayList 本质是通过一个可变长的数组来存储不定量的元素,且当元素通过不同方式被添加存储时,总是先计算自身容量,若符合扩容标准则对数组进行扩容并拷贝原有元素 本文将基于 JDK8 对 Arra ...

  6. 深深的码丨Java HashMap 源码透析

    Hashmap 的数据结构基础是基于一维数组实现的,向其添加元素时通过计算key的hash值来确定具体存储位置.添加元素过程中若出现hash冲突,也就是N个元素key的hash值相等,处理方式为:将元 ...

  7. linux下jdk源码安装,Linux JDK 源码安装

    一 环境 1.1 操作系统 [root@host-xxxsoft]# lsb_release -a LSB Version:    :base-4.0-amd64:base-4.0-noarch:co ...

  8. java jdk 1.8中lambda表达式常用方法

    在平常的开发工作当中,经常需要对数组进行一些操作,比如根据某个属性值分组,取出某个属性值作为数组等.那么,jdk 1.8为我们提供了便捷的方法,我们应该怎么使用呢? 1:filter:根据某个属性值过 ...

  9. Java JDK 源码结构

    oracle docs : oracle 文档 JDK 14 Documentation :jdk 文档 Java Platform, Standard Edition :java SE How to ...

最新文章

  1. エターナル キングダム ~滅びの魔女と伝説の剣~验证方法
  2. iOS pod init 报错
  3. MySQL 中 MyISAM 中的查询为什么比 InnoDB 快?
  4. NHibernate学习笔记(二):one-to-one关系映射
  5. 5000并发的qps是多少_高并发架构设计
  6. 图像旋转(信息学奥赛一本通-T1127)
  7. 网站抓取精灵V3.0正式版
  8. 介绍计算机课程英语作文,关于电脑课的英语作文
  9. 推荐5款心仪的电脑软件
  10. java mina框架_Mina框架在项目中的使用(一)
  11. 将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5
  12. OFC2020论文笔记 M1F.5 25.78-Gbit/s Burst-mode Receiver for 50G-EPON OLT
  13. 高质量代码的几大标准
  14. java json的使用_java JSON的使用和解析
  15. 基于springboot特色农产品电商平台毕业设计-附源码211515
  16. c语言——直接插入排序实现(时间复杂度与空间复杂度分析)
  17. 2021年气象为何反常?未来会是神马?
  18. 卡塔尔世界杯亚洲球队表现给我们的一些启示和思考
  19. java人职业规划(摘要) 1
  20. win10恢复上一次正确配置

热门文章

  1. java的移植性_详细介绍JAVA的可移植性
  2. 多线程处理缓慢_华为昇腾,AI推理性能超越对手一倍:软件挖掘处理器全部潜力...
  3. 大根堆的删除c语言,二叉堆(一)之 C语言详解
  4. mysql 导出中间 数据_MYSQL数据库之间的数据导出与导入
  5. http 错误 404.0 - not found_python3从零学习-5.10.8、http.client—HTTP 协议客户端
  6. SIFT和SURF的替换算法——ORB (Oriented FAST and Rotated BRIEF 快速定向和旋转)
  7. Pycharm报错合集:在pycharm运行anaconda配置的Pytorch环境报错(Environment location diretory is not empty )
  8. 【TensorFlow2.0】(5) 数学计算、合并、分割
  9. Learn OpenGL (七):摄像机
  10. Vue项目中使用wangEditor富文本输入框(推荐)