为什么覆重写equals hashcode的时候一定要覆写hashCode

编写高质量代码改善C#程序的157个建议——建议12:重写Equals时也要重写GetHashCode_C#应用_
编写高质量代码改善C#程序的157个建议——建议12:重写Equals时也要重写GetHashCode
来源:人气:70
建议12: 重写Equals时也要重写GetHashCode
除非考虑到自定义类型会被用作基于散列的集合的键值;否则,不建议重写Equals方法,因为这会带来一系列的问题。
如果编译上一个建议中的Person这个类型,编译器会提示这样一个信息:
&重写 Object.Equals(object o)但不重写 Object.GetHashCode()&
如果重写Equals方法的时候不重写GetHashCode方法,在使用如FCL中的Dictionary类时,可能隐含一些潜在的Bug。还是针对上一个建议中的Person进行编码,代码如下所示:
static Dictionary&Person, PersonMoreInfo& PersonValues = new Dictionary&Person, PersonMoreInfo&();
static void Main(string[] args)
AddAPerson();
Person mike = new Person("NB123");
//Console.WriteLine(mike.GetHashCode());
Console.WriteLine(PersonValues.ContainsKey(mike));
static void AddAPerson()
Person mike = new Person("NB123");
PersonMoreInfo mikeValue = new PersonMoreInfo() { SomeInfo = "Mike's info" };
PersonValues.Add(mike, mikeValue);
//Console.WriteLine(mike.GetHashCode());
Console.WriteLine(PersonValues.ContainsKey(mike));
本段代码的输出将会是:
理论上来说,在上一个建议中我们已经重写了Person的Equals方法;也就是说,在AddAPerson方法中的mike和Main方法中的 mike属于&值相等&。于是,将该&值&作为key放入Dictionary中,再在某处根据mike将mikeValue取出来,这会是理所当然的事 情。可是,从上面的代码段中我们发现,针对同一个示例,这种结论是正确的,若是针对不同的mike示例,这种结果就有问题了。
基于键值的集合(如上面的Dictionary)会根据Key值来查找Value值。CLR内部会优化这种查找,实际上,最终是根据Key值的 HashCode来查找Value值。代码运行的时候,CLR首先会调用Person类型的GetHashCode,由于发现Person没有实现 GetHashCode,所以CLR最终会调用Object的GetHashCode方法。将上面代码中的两行注释代码去掉,运行程序得到输出,我们会发 现,Main方法和AddAPerson方法中的两个mike的HashCode是不同的。这里需要解释为什么两者实际对应调用的 Object.GetHashCode会不相同。
Object为所有的CLR类型都提供了GetHashCode的默认实现。每new一个对象,CLR都会为该对象生成一个固定的整型值,该整型值 在对象的生存周期内不会改变,而该对象默认的GetHashCode实现就是对该整型值求HashCode。所以,在上面代码中,两个mike对象虽然属 性值都一致,但是它们默认实现的HashCode不一致,这就导致Dictionary中出现异常的行为。若要修正该问题,就必须重写 GetHashCode方法。Person类的一个简单的重写可以是如下的形式:
public override int GetHashCode()
return this.IDCode.GetHashCode();
此时再运行本条建议开始时代码的输出,就会发现两者的HashCode是一致的,而Dictionary也会找到相应的键值,输出:True。
细心的读者可能已经发现一个问题,Person类的IDCode属性是一个只读属性。从语法特性本身来讲,可以将IDCode设置为可写;然而从现 实的角度考虑,一个&人&一旦踏入社会,其IDCode就不应该改变,如果要改变,就相当于是另外一个人了。所以,我们应该只实现该IDCode的只读属 性。同理,GetHashCode方法也应该基于那些只读的属性或特性生成HashCode。
GetHashCode方法还存在另外一个问题,它永远只返回一个整型类型,而整型类型的容量显然无法满足字符串的容量,以下的例子就能产生两个同样的HashCode。
string str1 = "NB";
string str2 = "NB";
Console.WriteLine(str1.GetHashCode());
Console.WriteLine(str2.GetHashCode());
为了减少两个不同类型之间根据字符串产生相同的HashCode的几率,一个稍作改进版本的GetHashCode方法如下:
public override int GetHashCode()
return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDCode).GetHashCode();
注意 重写Equals方法的同时,也应该实现一个类型安全的接口IEquatable,所以Person类型的最终版本应该如下所示:
class Person : IEquatable&Person&
public string IDCode { get; ivate set; }
public Person(string idCode)
this.IDCode = idC
public override bool Equals(object obj)
return IDCode == (obj as Person).IDC
public override int GetHashCode()
return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + this.IDCode).GetHashCode();
public bool Equals(Person other)
return IDCode == other.IDC
转自:《编写高质量代码改善C#程序的157个建议》陆敏技
优质网站模板21662人阅读
&& &经常在论坛上面看到覆写hashCode函数的问题,很多情况下是一些开发者不了解hash code,或者和equals一起用的时候不太清楚为啥一定要覆写hashCode。
&& &对于hash code的理论我不想多说,这个话题太大。那些&对称性&,&传递性&的规则网上有无数的文章来描述。我只想说用hash code的原因只有一个:效率。理论的说法它的复杂度只有O(1)。试想我们把元素放在线性表里面,每次要找一个元素必须从头一个一个的找它的复杂度有O(n)。如果放在平衡二叉树,复杂度也有O(log n)。
&& 为啥很多地方说&覆写equals的时候一定要覆写hashCode&。说到这里我知道很多人知道有个原则:如果a.equals(b)那么要确保a.hashCode()==b.hashCode()。为什么?hashCode和我写的程序的业务逻辑毫无关系,为啥我要override?&要我说如果你的class永远不可能放在hash code为基础的容器内,不必劳神,您真的不必override hashCode() :)
&& &说得准确一点放在HashMap和Hashtable里面如果是作为value而不是作为key的话也是不必override hashCode了。至于HashSet,实际上它只是忽略value的HashMap,每次HashSet.add(o)其实就是HashMap.put(o, dummyObject)。
&& &那为什么放到Hash容器里面要overide hashCode呢?因为每次get的时候HashMap既要看equals是不是true也要看hash code是不是一致,put的时候也是要看equals和hash code。
&& &如果说到这里您还是不太明白,咱就举个例子:
&& &譬如把一个自己定义的class Foo{...}作为key放到HashMap。实际上HashMap也是把数据存在一个数组里面,所以在put函数里面,HashMap会调Foo.hashCode()算出作为这个元素在数组里面的下标,然后把key和value封装成一个对象放到数组。等一下,万一2个对象算出来的hash code一样怎么办?会不会冲掉?先回答第2个问题,会不会冲掉就要看Foo.equals()了,如果equals()也是true那就要冲掉了。万一是false,就是所谓的collision了。当2个元素hashCode一样但是equals为false的时候,那个HashMap里面的数组的这个元素就变成了链表。也就是hash code一样的元素在一个链表里面,链表的头在那个数组里面。
回过来说get的时候,HashMap也先调key.hashCode()算出数组下标,然后看equals如果是true就是找到了,所以就涉及了equals。
&& &假设如果有个key为a的元素在HashMap里面的情况:
&& &1:如果这时候用equals为true但是hashCode不等的b作为get参数的话,这个时候b算出来的数组下标一定不是a所在的下标位置。
&& &2:如果这时候用equals为false但是hashCode相等的b作为get参数的话,这个时候b算出来的数组下标是对了,但是用equals来寻找相符的key就找不到a了。
&& &以上2种情况要么就是get找不到符合的元素返回null,要么就是返回一个hashCode和equals恰好都符合b的另外的元素,这就产生了混乱。混乱的根本就是错误实现hashCode和equals。
&&下面有个非常简化版的HashMap实现帮助大家理解,您甚至可以把它当作伪代码来看。(这个实现只是为了模拟HashMap的机制,所以参数检查,访问修饰符都忽略。在java.util.HashMap里面的对hash值的二次hash,根据数组长度计算index,以及超出数组时的resize都忽略)
&textarea cols=&50& rows=&15& name=&code& class=&java&&/*
* Just to demonstrate hash map mechanism,
* Please do not use it in your commercial product.
* Argument checking and modifier are ignored.
* In java.util.HashMap, array are used instead of ArrayList, so index of array is calculated by 'h & (length-1)',
* while we use ArrayList to skip the calculation for simple.
* @author Shengyuan Lu 卢声远 &.cn&
public class SimpleHashMap {
ArrayList&LinkedList&Entry&& entries = new ArrayList&LinkedList&Entry&&();
* Each key-value is encapsulated by Entry.
static class Entry {
public Entry(Object key, Object value) {
this.key =
this.value =
void put(Object key, Object value) {
LinkedList&Entry& e = entries.get(key.hashCode());
if (e != null) {
for (Entry entry : e) {
if (entry.key.equals(key)) {
entry.value =// Match in lined list
e.addFirst(new Entry(key, value));// Add the entry to the list
// Put the new entry in array
LinkedList&Entry& newEntry = new LinkedList&Entry&();
newEntry.add(new Entry(key, value));
entries.add(key.hashCode(), newEntry);
Object get(Object key) {
LinkedList&Entry& e = entries.get(key.hashCode());
if (e != null) {
for (Entry entry : e) {
if (entry.key.equals(key)) {
return entry.
* Do we need to override equals() and hashCode() for SimpleHashMap itself?
* I don't know either:)
}&/textarea&
这个问题的权威阐释可以参考Bloch的&Effective Java&的 Item 9: Always override&hashCode&when you override&equals
推荐下篇采访:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:127620次
积分:1497
积分:1497
排名:第18896名
原创:21篇
评论:185条
(1)(4)(1)(1)(3)(5)(2)(2)(1)(1)(3)Java 中正确使用 hashCode 和 equals 方法 - 开源中国社区
当前访客身份:游客 [
当前位置:
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解。我将讨论他们的默认实现,以及如何正确的重写他们。我也将使用Apache Commons提供的工具包做一个实现。
hashCode()和equals()的用法
重写默认实现
使用Apache Commons Lang包重写hashCode()和equals()
需要注意记住的事情
当使用ORM的时候特别要注意的
hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。
使用hashCode()和equals()
hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。
重写默认的实现
如果你不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。
来看看这个例子,让我们创建一个简单的类Employee
public class Employee
private String lastN
public Integer getId() {
public void setId(Integer id) {
public String getFirstname() {
public void setFirstname(String firstname) {
this.firstname =
public String getLastName() {
return lastN
public void setLastName(String lastName) {
this.lastName = lastN
public String getDepartment() {
public void setDepartment(String department) {
this.department =
} 上面的Employee类只是有一些非常基础的属性和getter、setter.现在来考虑一个你需要比较两个employee的情形。
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Prints false in console
System.out.println(e1.equals(e2));
} 毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个employee。真正的商业逻辑希望我们返回true。
为了达到这个目的,我们需要重写equals方法。
public boolean equals(Object o) {
if(o == null)
if (o == this)
if (getClass() != o.getClass())
Employee e = (Employee)
return (this.getId() == e.getId());
} 在上面的类中添加这个方法,EauqlsTest将会输出true。
So are we done?没有,让我们换一种测试方法来看看。
import java.util.HashS
import java.util.S
public class EqualsTest
public static void main(String[] args)
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Prints 'true'
System.out.println(e1.equals(e2));
Set&Employee& employees = new HashSet&Employee&();
employees.add(e1);
employees.add(e2);
//Prints two objects
System.out.println(employees);
} 上面的程序输出的结果是两个。如果两个employee对象equals返回true,Set中应该只存储一个对象才对,问题在哪里呢?
我们忘掉了第二个重要的方法hashCode()。就像JDK的Javadoc中所说的一样,如果重写equals()方法必须要重写hashCode()方法。我们加上下面这个方法,程序将执行正确。
public int hashCode()
final int PRIME = 31;
int result = 1;
result = PRIME * result + getId();
使用Apache Commons Lang包重写hashCode() 和equals()方法
Apache Commons 包提供了两个非常优秀的类来生成hashCode()和equals()方法。看下面的程序。
import mons.lang3.builder.EqualsB
import mons.lang3.builder.HashCodeB
public class Employee
private String lastN
public Integer getId() {
public void setId(Integer id) {
public String getFirstname() {
public void setFirstname(String firstname) {
this.firstname =
public String getLastName() {
return lastN
public void setLastName(String lastName) {
this.lastName = lastN
public String getDepartment() {
public void setDepartment(String department) {
this.department =
public int hashCode()
final int PRIME = 31;
return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
toHashCode();
public boolean equals(Object o) {
if (o == null)
if (o == this)
if (o.getClass() != getClass())
Employee e = (Employee)
return new EqualsBuilder().
append(getId(), e.getId()).
isEquals();
} 如果你使用Eclipse或者其他的IDE,IDE也可能会提供生成良好的hashCode()方法和equals()方法。
需要注意记住的事情
尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。在我们的案例中,我们使用员工id。
eqauls方法必须保证一致(如果对象没有被修改,equals应该返回相同的值)
任何时候只要a.equals(b),那么a.hashCode()必须和b.hashCode()相等。
两者必须同时重写。
当使用ORM的时候特别要注意的
如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量。因为在ORM中有的时候成员变量会被延时加载,这些变量只有当getter方法被调用的时候才真正可用。
例如在我们的例子中,如果我们使用e1.id == e2.id则可能会出现这个问题,但是我们使用e1.getId() == e2.getId()就不会出现这个问题。
希望这篇文章能够帮助你。
共有42个评论
<span class="a_vote_num" id="a_vote_num_
好文,第一次清楚的搞明白了
<span class="a_vote_num" id="a_vote_num_
比较清楚了,谢谢。
<span class="a_vote_num" id="a_vote_num_
不明白java的设计者为啥要搞出个hashCode()方法,一个equals()不是足够了吗?
--- 共有 4 条评论 ---
hash散列算法,使得在hash表中查找一个记录速度变O(1). 每个记录都有自己的hashcode,散列算法按照hashcode把记录放置在合适的位置. 在查找一个记录,首先先通过hashcode快速定位记录的位置.然后再通过equals来比较是否相等. 没有hashcode,一个一个比较过来,时间就变O(N)了. 不知道说清楚了没? 也可以参照Index.
(4年前)&nbsp&
: 但是从机器的角度来说,相同的内容在内存中只有一份才是最优设计吧
(4年前)&nbsp&
: 我想问设计者为不让这些类调用equals不是同样能比较相等吗?
上面说覆写了equals就必须覆写hashCode,那不是意味着,若人为定义两对象相等,则其内存地址也让其相等,这显然不符合人的常规思维。从人的常规思维来说,两个对象定义为相等,其内存地址可以不等,也可以相等。
(4年前)&nbsp&
hashCode()是HashTable、HashMap和HashSet使用的
(4年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
引用来自“喜之郎”的答案
不明白java的设计者为啥要搞出个hashCode()方法,一个equals()不是足够了吗?
hash code跟内存没有关系,只不过是Object的默认hashCode()方法会返回一个内存编号,因为这样一定满足hashCode()方法的要求。
hashCode()方法要求:
当对象状态未改变,那么多次调用返回的值必须相等 两个对象equal,那么对象调用返回的值必须相等
你说的那个人的常规思维是认为hash code跟内存相关联,实际上不是,你可以理解为一个数字标识当前对象状态
--- 共有 3 条评论 ---
: 学习了。
(4年前)&nbsp&
: 一个set有一万个元素,再加入一个不同的新元素,则需要执行一万次的equal方法,这个效率太低了。所以采用先比较hashcode,由于是64位整数,可以建立索引查找。如果hashcode没找到,则必定不equal,加入set当中;即使找到了,也只需执行hashcode相同的几个元素的equal。这是一种性能设计。
(4年前)&nbsp&
那人为什么要关心“对象的状态是否改变”呢?
(4年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
好文,不错
<span class="a_vote_num" id="a_vote_num_
引用来自“JekyCui”的答案
引用来自“喜之郎”的答案
不明白java的设计者为啥要搞出个hashCode()方法,一个equals()不是足够了吗?
hash code跟内存没有关系,只不过是Object的默认hashCode()方法会返回一个内存编号,因为这样一定满足hashCode()方法的要求。
hashCode()方法要求:
当对象状态未改变,那么多次调用返回的值必须相等 两个对象equal,那么对象调用返回的值必须相等
你说的那个人的常规思维是认为hash code跟内存相关联,实际上不是,你可以理解为一个数字标识当前对象状态
应当是同一个对象要确保hashcode不变吧&
The value returned by hashCode is an int that maps an object into a bucket in a hash table.
An object must always produce the same hash code. However, objects can share hash codes (they aren’t necessarily unique). Writing a “correct” hashing function is easy—always return the same hash code for the same object. Writing an “efficient” hashing function—one that provides a sufficient distribution of objects over the buckets—is difficult and is outside the scope of this book.
--- 共有 2 条评论 ---
: 讨论嘛..另外如果同一个对象要保持hash code一致,显然使用最原始的object的地址码最好,就不用覆盖了
(4年前)&nbsp&
你说的对 我说错了 不好意思
我又查了些资料
(4年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
equals方法的使用者是我们,而hashcode方法是jdk(如往list中插入值,会根据code决定顺序,这是equals方法无法代替的)。整体来说,equals相等,那么hashcode必须一样。而hashcode一样,equals不一定一样。
--- 共有 2 条评论 ---
个人拙见,hashcode就像是对象的标识码,可以根据对象的属性计算而出。
(4年前)&nbsp&
让equals返回int的-1,0,1就行啊。
(4年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
手痒,弄上String的equals源码
public boolean equals(Object anObject) { if (this == anObject) { & & } if (anObject instanceof String) { & &String anotherString = (String)anO & &int n = & &if (n == anotherString.count) { char v1[] = char v2[] = anotherString. int i = int j = anotherString. while (n-- != 0) { & &if (v1[i++] != v2[j++]) } & &} } & & }
<span class="a_vote_num" id="a_vote_num_
第一次 搞明白 & hashcode &equal &呵呵 &非常感谢
<span class="a_vote_num" id="a_vote_num_
更多开发者职位上
有什么技术问题吗?
王振威的其它问题
类似的话题[求助]为什么重写equals方法,一定要重写HashCode方法?
[问题点数:10分,结帖人Steve_Samuelson]
[求助]为什么重写equals方法,一定要重写HashCode方法?
[问题点数:10分,结帖人Steve_Samuelson]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2003年9月 C/C++大版内专家分月排行榜第二2002年6月 C/C++大版内专家分月排行榜第二2002年4月 C/C++大版内专家分月排行榜第二
2007年3月 C/C++大版内专家分月排行榜第三2007年2月 C/C++大版内专家分月排行榜第三2007年1月 C/C++大版内专家分月排行榜第三2003年7月 C/C++大版内专家分月排行榜第三
2003年9月 C/C++大版内专家分月排行榜第二2002年6月 C/C++大版内专家分月排行榜第二2002年4月 C/C++大版内专家分月排行榜第二
2007年3月 C/C++大版内专家分月排行榜第三2007年2月 C/C++大版内专家分月排行榜第三2007年1月 C/C++大版内专家分月排行榜第三2003年7月 C/C++大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。您所在的位置: &
建议48: 覆写equals方法必须覆写hashCode方法
建议48: 覆写equals方法必须覆写hashCode方法
机械工业出版社
《编写高质量代码:改善Java程序的151个建议》第3章类、对象及方法,本章主要讲述关于Java类、对象、方法的种种规则、限制及建议,让读者在面向对象编程的世界中走得更远,飞得更高。本节为大家介绍建议48: 覆写equals方法必须覆写hashCode方法。
建议48: 覆写equals方法必须覆写hashCode方法
覆写equals方法必须覆写hashCode方法,这条规则基本上每个Javaer都知道,这也是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?本建议就来解释该问题,我们先来看如下代码:public&static&void&main(String[]&args)&{ &&&&&&//&Person类的实例作为Map的key &&&&&&Map,&Object&map&=&new&HashMap,&Object()&{ &&&&&&&&&{ &&&&&&&&&&&&&put(new&Person("张三"),&new&Object()); &&&&&&&&&} &&&&&&}; &&&&&&//&Person类的实例作为List的元素 &&&&&&List&list&=&new&ArrayList()&{ &&&&&&&&&{ &&&&&&&&&&&&&add(new&Person("张三")); &&&&&&&&&} &&&&&&}; &&&&&&//&列表中是否包含 &&&&&&boolean&b1&=&list.contains(new&Person("张三")); &&&&&&//&Map中是否包含 &&&&&&boolean&b2&=&map.containsKey(new&Person("张三")); &}&
代码中的Person类与上一建议相同,euqals方法完美无缺。在这段代码中,我们在声明时直接调用方法赋值,这其实也是一个内部匿名类的操作(下一个建议会详细说明)。现在的问题是b1和b2这两个boolean值是否都为true?
我们先来看b1,Person类的equals覆写了,不再判断两个地址是否相等,而是根据人员的姓名来判断两个对象是否相等,所以不管我们的new Person(“张三”)产生了多少个对象,它们都是相等的。把“张三”对象放入List中,再检查List中是否包含,那结果肯定是true了。
接着来看b2,我们把张三这个对象作为了Map的键(Key),放进去的对象是张三,检查的对象还是张三,那应该和List的结果相同了,但是很遗憾,结果是false。原因何在呢?
原因就是HashMap的底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组下标的处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到Map条目的链表中。同理,检查键是否存在也是根据哈希码确定位置,然后遍历查找键值的。
接着深入探讨,那对象元素的hashCode方法返回的是什么值呢?它是一个对象的哈希码,是由Object类的本地方法生成的,确保每个对象有一个哈希码(这也是哈希算法的基本要求:任意输入k,通过一定算法f(k),将其转换为非可逆的输出,对于两个输入k1和k2,要求若k1=k2,则必须f(k1)=f(k2),但也允许k1≠k2,f(k1)=f(k2)的情况存在)。
那回到我们的例子上,由于我们没有重写hashCode方法,两个张三对象的hashCode方法返回值(也就是哈希码)肯定是不相同的了,在HashMap的数组中也就找不到对应的Map条目了,于是就返回了false。
问题清楚了,修改也非常简单,重写一下hashCode方法即可,代码如下:class&Person&{ &&&&&/*其他代码相同,不再赘述*/ &&&&&@Override &&&&&public&int&hashCode()&{ &&&&&&&&&return&new&HashCodeBuilder().append(name).toHashCode(); &&&&&} &}&
其中HashCodeBuilder是mons.lang.builder包下的一个哈希码生成工具,使用起来非常方便,诸位可以直接在项目中集成。(为什么不直接写hashCode方法?因为哈希码的生成有很多种算法,自己写麻烦,事儿又多,所以采用拿来主义是最好的方法。)
【责任编辑: TEL:(010)】&&&&&&
关于&&&&的更多文章
Play Framework是一个Rails风格full-stack(全栈的)Java Web应
本书描述了黑客用默默无闻的行动为数字世界照亮了一条道路的故事。
不管是创建桌面程序、构建Web应用或是开发自己的Windo
多年来,Imar Spaanjaars一直是ASP.NET相关图书的畅销
《C#高级编程(第8版)》是C# 2012和.NET 4.5高级技术的
本书紧紧围绕“软件架构设计”这一主题,立足实践解析了软件架构的概念,阐述了切实可行的软件架构设计方法,提供了可操作性极强
51CTO旗下网站}

我要回帖

更多关于 java equals hashcode 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信