一containsAll方法
今天上班的时候遇到一个坑,我把java list集合中存入对象,然后用containsAll 方法进行比较时,明明一个List集合包含另一个List集合的元素,但是结果总是返回 false。
1部分代码块如下
@Override
@Transactional(rollbackFor = Exception.class)
public boolean validate(Account account) {
boolean flag = false;
String acc = account.getAccount();
String pass = account.getPassword();
if (acc != "" && pass != "") {
AccountExample accountExampleAcc = new AccountExample();
accountExampleAcc.createCriteria().andAccountEqualTo(acc);
List<Account> accs1 = accountMapper.selectByExample(accountExampleAcc);
AccountExample accountExamplePass = new AccountExample();
accountExamplePass.createCriteria().andPasswordEqualTo(pass);
List<Account> accs2 = accountMapper.selectByExample(accountExamplePass);
if (accs2.containsAll(accs1)&&accs1.size()!=0&&accs2.size()!=0) {
flag = true;
} else {
flag = false;
}
return flag;
} else {
return false;
}
}
2查看 containsAll()方法源码
containsAll():
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
contains(e) :
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
indexOf(o):
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
equals:
public boolean equals(Object obj) {
return (this == obj);
}
到这里发现到equals调用的是Object.java 的equals方法,比较对象,比较对象的内存地址,我们知道即使相等的对象内存地址并不相同,所以一定会false;
二 equals方法
1Java中equals和==的区别
java中的数据类型,可分为两类:
①.基本数据类型,也称原始数据类型。
byte,short,char,int,long,float,double,boolean
对于基本类型来说,他们存储在jvm的栈中,因此 他们之间的比较,应用双等号(==),比较的是变量的内容,也就是比较的变量的值。
②.复合数据类型,也称引用数据类型
String,Integer,Date,List<>,Map................
对于引用类型来说, JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址, 对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值,因为对象内容存储在jvm的堆中,栈中只是存储对象的引用(地址,地址的类型为字符串),即栈中地址字符串是引用堆的地址,所以无论是==还是equals比较的都是栈中的内容,即对象的引用,也就是比较的是两个对象的地址。除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。
但是根据创建对象的方式不同可以分为两种情况:
Integer使用表达式创建对象:equals方法比较之后相等
.
Integer使用new方法创建对象:equals方法比较之后不相等
③这里会引入两个新的问题
1.为什么表达式创建和new创建,会让==比较产生不同的结果。
这是因为jvm在程序运行的时候会创建一个缓冲池,当使用表达式创建的时候,程序会在缓冲池中寻找相同值的对象,如果找到,就把这个对象的地址赋给当前创 建的对象,因此,c和d实际上都指向了c的引用。因此在使用==时会返回true。
当用new创建对象时,是在堆中重新分配内存,因此栈中的引用是不相同的,所以,a和b引用的是值相同的不同对象。所以a = =b返回false
2.既然equals比较的是引用,那么a.equals(b)为什么返回true。
因为在一些Object的子类库当中这个方法被覆盖掉了,如String,Integer,Date,在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了,而是直接比较值。 即在Integer里,重写了equals方法!
2重写equal方法
所以List加入是Account 对象的话,如果要使用containsAll方法时,在Account对象中重写equals 方法即可;
/**
* Created by @author LiuChunhang on 2020/7/18.
*/
package fengbo.entity;
import java.io.Serializable;
import java.util.Objects;
/**
* Created by @author LiuChunhang on 2020/7/14.
*/
public class Account implements Serializable {
private Integer id;
private String account;
private String password;
private String enterprise;
private String phone;
private String mail;
private int status;
public Account() {
}
public Account(Integer id, String account, String password, String enterprise, String phone, String mail, int status) {
this.id = id;
this.account = account;
this.password = password;
this.enterprise = enterprise;
this.phone = phone;
this.mail = mail;
this.status = status;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account == null ? null : account.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public String getEnterprise() {
return enterprise;
}
public void setEnterprise(String enterprise) {
this.enterprise = enterprise == null ? null : enterprise.trim();
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone == null ? null : phone.trim();
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail == null ? null : mail.trim();
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
", enterprise='" + enterprise + '\'' +
", phone='" + phone + '\'' +
", mail='" + mail + '\'' +
", status=" + status +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Account account1 = (Account) o;
return status == account1.status &&
id.equals(account1.id) &&
account.equals(account1.account) &&
password.equals(account1.password) &&
enterprise.equals(account1.enterprise) &&
phone.equals(account1.phone) &&
mail.equals(account1.mail);
}
@Override
public int hashCode() {
return Objects.hash(id, account, password, enterprise, phone, mail, status);
}
}
上面的代码展示了Student类的重写后的equals方法和hashcode方法,建议大家用eclipse或idea自动生成,尽量不要自己敲因为很有可能会出错。
三重写equals一定要重写hashcode
1以下是关于hashcode的一些规定
两个对象相等,hashcode一定相等
两个对象不等,hashcode不一定不等
hashcode相等,两个对象不一定相等
hashcode不等,两个对象一定不等
2 重写hashcode的原因
所以我们重写过equals方法之后,让两个内存地址不一样的对象相等之后,他们的hashcode值也应该相等!
但是默认的hashcode方法是根据对象的内存地址经哈希算法得来的,他们的hashcode不一定相等,所以我们才有了重写hashcode的必要性!
3总结
哈希集合要保证元素唯一性吧。对象放入集合,先经过hash运算,得到index,找到数组对应位置,还要和此位置的对象进行比较,不一样就插入到后面形成链表,一样的话就不用插入了。比较用到的是equals方法。重写了equals()方法,但不重写hashcode()方法,那可能两个相同的对象放入到了不同的位置,元素的唯一性就不存在了。