我们知道list存储的是有序不唯一的元素。
set存储的是无序唯一的元素。
那么下面看一个例子吧:
package CollectionPart; import java.util.HashSet; import java.util.Set; public class HashSet1 { public static void main(String[] args) { Set mySet = new HashSet(); mySet.add("df"); mySet.add("df"); System.out.println(mySet.size()); for (Object object : mySet) { System.out.println(object); } } }
运行结果:
1
df
这就充分说明了,set集合里面存储的元素是唯一的。
那么如下代码将会反映一个问题:
package CollectionPart; import java.util.HashSet; import java.util.Set; public class HashSet1 { public static void main(String[] args) { Set mySet = new HashSet(); mySet.add("df"); mySet.add("df"); Employee_1 e1 = new Employee_1("1","lifei",23,"2013-02-09"); Employee_1 e2 = new Employee_1("1","lifei",23,"2013-02-09"); mySet.add(e1); mySet.add(e2); System.out.println(mySet.size()); for (Object object : mySet) { System.out.println(object); } } }
运行结果:
3
df
Employee_1 [employeeId=1, employeeName=lifei, employeeAge=23, employeeHireDate=2013-02-09]
Employee_1 [employeeId=1, employeeName=lifei, employeeAge=23, employeeHireDate=2013-02-09]
我们存储的元素明明一模一样,但是结果好像不如人意,不是说好set里面存储的都是不同的元素么,e1 跟e2明显是 同一个对象。
这就需要考量set集合在判定元素是否相等的时候,是通过什么进行判别的。
鼠标移到这个 add方法上面就会有提示:
Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.
这个说明还是有必要读一下的:添加特定元素给set集合,如果不存在这样一个元素的时候。通常情况下,为当前set集合添加元素e时,如果这个set集合没有已经存在一个e2的话,【通过equals函数来比较】如果已经存在此元素的话,那么add方法的调用并不会修改set集合,并且会返回false。
//有两个点要试:1、null的值可以存储几个,从说明上感觉是1个。2、第二次添加已经存在的元素的时候,会报添加失败,而不是第二次的值覆盖第一次的值。这个是纯想象的,因为覆盖就有添加了一次,这感觉上会产生更多的操作。不如不允许添加来的效率高。
package CollectionPart; import java.util.HashSet; import java.util.Set; public class HashSet1 { public static void main(String[] args) { Set mySet = new HashSet(); mySet.add("df"); boolean add = mySet.add("df"); System.out.println("是否添加成功:"+add); Employee_1 e1 = new Employee_1("1","lifei",23,"2013-02-09"); Employee_1 e2 = new Employee_1("1","lifei",23,"2013-02-09"); mySet.add(e1); mySet.add(e2); System.out.println(mySet.size()); for (Object object : mySet) { System.out.println(object); } mySet.add(null); boolean add2 = mySet.add(null); System.out.println("是否添加成功:"+add2+",此时set集合中的元素个数:"+mySet.size()); } }
运行结果:
是否添加成功:false
3
df
Employee_1 [employeeId=1, employeeName=lifei, employeeAge=23, employeeHireDate=2013-02-09]
Employee_1 [employeeId=1, employeeName=lifei, employeeAge=23, employeeHireDate=2013-02-09]
是否添加成功:false,此时set集合中的元素个数:4
接下来就要处理,这个 equals的问题了,就可以回去考究这个 set集合中对于元素的equals的方法的比较了,毕竟,e1 跟e2 是一个人。要想办法,把它们区分开。查看默认的 Employee中的equals方法
我们所写的员工类里面并没有一个 equals方法,说明这是 继承来的:所以敲出equals 坐等联想然后得到这样一个函数:
@Override public boolean equals(Object obj) { // TODO Auto-generated method stub return super.equals(obj); }
鼠标放在 equals上面得到这样一个说明:
Indicates whether some other object is "equal to" this one.
The equals
method implements an equivalence relation on non-null object references:
- It is reflexive: for any non-null reference value
x
,x.equals(x)
should returntrue
. - It is symmetric: for any non-null reference values
x
andy
,x.equals(y)
should returntrue
if and only ify.equals(x)
returnstrue
. - It is transitive: for any non-null reference values
x
,y
, andz
, ifx.equals(y)
returnstrue
andy.equals(z)
returnstrue
, thenx.equals(z)
should returntrue
. - It is consistent: for any non-null reference values
x
andy
, multiple invocations ofx.equals(y)
consistently returntrue
or consistently returnfalse
, provided no information used inequals
comparisons on the objects is modified. - For any non-null reference value
x
,x.equals(null)
should returnfalse
.
The equals
method for class Object
implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x
and y
, this method returns true
if and only if x
and y
refer to the same object (x == y
has the value true
).
Note that it is generally necessary to override the hashCode
method whenever this method is overridden, so as to maintain the general contract for the hashCode
method, which states that equal objects must have equal hash codes.
简单翻译一下:欢迎斧正,
判断其他的对象是否与当前对象相等。
反射性?reflexive?:对于任何值,x.equals(s) 应该返回true.
对称性:对于非空值x,y,如果x.equals(y)返回true时,当且仅当y.equals(x)也返回true.
传递性:对于非空值x,y和z,如果 x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)应该也返回true.
一致性?:不论怎么调用,结果都应该是一致的,要么true,要么false,当参与比较的两个非空值,x,y,在这种不断地比较过程中始终未被修改的时候。
对于非空值 x,x.equals(null) 的返回值应该是false.
equals方法用来描述object类中两个对象是否相等的关系,如果非空值x,y的返回结果是true的话,那么x,y执行的是相同的实体。
注:通常情况下也要重写hashCode方法,只要equals方法被重写。为了hashCode方法保持总体联系,为了标明相同的对象具有相同的hash码。
所以,这才是 Set集合要注意的地方,只要使用Set集合的话,就要记得重写equals 方法和 hashCode方法。【好在这两个方法都可以自动生成。】
在ssh里面,如果用到多对多的情况的时候,要记得重写这两个方法。
不过重点好像还是没有解释清楚。就是 e1 究竟为什么不等于e2.
我们通过快捷键ctrl+鼠标左键,找到他的实现方法:
public boolean equals(Object obj) { return (this == obj); }
发现。他们在底层调用的就是这样的语句,那么问题来了,这比较的是什么,this指代的是什么?我们通常在校验是否空指针的时候总会,输出一下,当前对象。那么这个比较就是返回的当前这个 对象的地址值,如果 这个 值不相等的话,就认为两者不相等。好了,那么现在我们认为 id相同并且,姓名相同的话,这两个元素就是一个实体,事实上,最好把所有属性都包含进去,但是 可以利用后期系统的某些存在唯一性约束的属性,作为两个实体是否相等的标识。
Set集合要说明的就是这一个一定要重写 equals() 方法和 hashCode()方法,别的没有了。
习题:打印员工姓名及性别,利用迭代器遍历
员工的字段有 工号 姓名 年龄 入职时间。
由于改写了 员工类的 不妨新写一个 实体类。
//写的时候 复制的,所以 就输出 姓名和年龄吧
package CollectionPart; /** * 为了Set的联系,比Employee_1多重写了两个方法 * */ public class Employee_2 { private String employeeId; private String employeeName; private int employeeAge; private String employeeHireDate; /* 这里就存储成String 类型,在 数据库里面设置成 date格式。 然后 利用Date today = new Date(); SimpleDateFormat fm = new SimpleDateFormat("YYYY-MM-dd"); 来做 */ public String getEmployeeId() { return employeeId; } public void setEmployeeId(String employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public int getEmployeeAge() { return employeeAge; } public void setEmployeeAge(int employeeAge) { this.employeeAge = employeeAge; } public String getEmployeeHireDate() { return employeeHireDate; } public void setEmployeeHireDate(String employeeHireDate) { this.employeeHireDate = employeeHireDate; } @Override public String toString() { return "Employee_1 [employeeId=" + employeeId + ", employeeName=" + employeeName + ", employeeAge=" + employeeAge + ", employeeHireDate=" + employeeHireDate + "]"; } public Employee_2(String employeeId, String employeeName, int employeeAge, String employeeHireDate) { super(); this.employeeId = employeeId; this.employeeName = employeeName; this.employeeAge = employeeAge; this.employeeHireDate = employeeHireDate; } public Employee_2() { super(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + employeeAge; result = prime * result + ((employeeHireDate == null) ? 0 : employeeHireDate.hashCode()); result = prime * result + ((employeeId == null) ? 0 : employeeId.hashCode()); result = prime * result + ((employeeName == null) ? 0 : employeeName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee_2 other = (Employee_2) obj; if (employeeAge != other.employeeAge) return false; if (employeeHireDate == null) { if (other.employeeHireDate != null) return false; } else if (!employeeHireDate.equals(other.employeeHireDate)) return false; if (employeeId == null) { if (other.employeeId != null) return false; } else if (!employeeId.equals(other.employeeId)) return false; if (employeeName == null) { if (other.employeeName != null) return false; } else if (!employeeName.equals(other.employeeName)) return false; return true; }}
package CollectionPart; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetPractise_1 { public static void main(String[] args) { Employee_2 e1= new Employee_2("dept1_001", "lifei", 23, "2016-02-05"); Employee_2 e2= new Employee_2("dept1_001", "lifei", 23, "2016-02-05"); Employee_2 e3= new Employee_2("dept1_002", "life2", 24, "2016-03-05"); Employee_2 e4= new Employee_2("dept3_001", "life3", 28, "2015-03-05"); Set<Employee_2> mySet = new HashSet<Employee_2>(); mySet.add(e1); mySet.add(e2); mySet.add(e3); mySet.add(e4); Iterator<Employee_2> iterator = mySet.iterator(); System.out.println("员工人数为:"+mySet.size()); while (iterator.hasNext()) { Employee_2 employee_2 = iterator.next(); System.out.println("当前员工: "+employee_2.getEmployeeName()+" ,年龄为:"+employee_2.getEmployeeAge()); } } }
运行结果:
员工人数为:3
当前员工: life3 ,年龄为:28
当前员工: lifei ,年龄为:23
当前员工: life2 ,年龄为:24
发现一个事儿,就是 确实是无序的。