无序集合
无序:展示顺序与存储顺序不一致
不能保存重复数据
重复数据:基本数据类型、String类型 值相同 就是重复数据
引用数据类型:默认比较地址 采用父类的hashCode()
可以通过重写去重的方法【equals() hashCode()】来实现忽略地址 只比较属性值
java中是如何实现真正的去重:通过equals() hashCode()两个方法联合使用
Object的hashCode():表示在哈希表中的位置 根据所存储的物理地理位置通过一定算法获取 整数
Object的toString():返回字符串中@后的内容就是当前对象通过hashCode()获取的整数的十六进制表示形式
User u1 = new User("gasdf",18);
System.out.println(u1.hashCode()); //98127626
System.out.println(u1); //com.woniuxy.demo1.User@5d94f0a
案例:
User自定义类型 String name int age
创建HashSet 保存User对象 只要是new的新对象 都认为是不同数据 跟name age是否相同无关
现在希望忽略地址 只要属性值都相同 则认为是重复数据 不让HashSet存储
可以重写hashCode() 和 equals()
public class User {
public String name;
public int age;
public User(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
//通过快捷键shift+alt+s 生成hashCode()和equals()方法关于属性的重写
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.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;
User other = (User) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
//测试类中
User u1 = new User("gasdf",18);
User u2 = new User("gasdf",18);
User u3 = new User("gasdf",18);
User u4 = new User("gasdf",18);
User u5 = new User("gasdf",18);
User u6 = u1;
//HashSet集合 存储元素时 会根据hashCode()和equals()来判定是否是重复数据
HashSet<User> hu = new HashSet<User>();
hu.add(u1);
hu.add(u2);
hu.add(u3);
hu.add(u4);
hu.add(u5);
hu.add(u6);
hu.add(u1);
for(User u : hu) {
System.out.println(u);
//如果没有重写去重方法 则有5个数据
//如果重写了去重方法 则只有1个数据
}
HashSet
基本数据类型、String 已经重写了去重方法 可以自动去重
自定义类型:重写去重方法【hashCode() equals()】
比较器失效—》无序集合 不能通过Collections.sort()进行排序 可以转成有序集合
LinkedHashSet
HashSet的子实现类
每个元素另外保存有下个元素的地址(多了一根保留顺序的链条) --》展示顺序与存储顺序一致 依然没有索引
基本数据类型、String 已经重写了去重方法 可以自动去重
自定义类型:重写去重方法【hashCode() equals()】
比较器失效—》无序集合 不能通过Collections.sort()进行排序 可以转成有序集合
TreeSet 存储的数据类型 必须实现了比较器接口 否则存储失败
存储数据时 会根据比较器 判断两个元素是否相同及先后顺序(比较器实现去重和排序两个功能)
基本数据类型、String类型 直接存储 默认去重
自定义类型:去重依赖比较器 如果两个元素通过比较器判断返回0 则认为是重复数据 会执行去重
反之 即使两个元素地址 属性值都相同 如果比较返回非0 则认为是不同数据 都会存储
public class User implements Comparable<User>{
String name;
int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
// @Override
// public String toString() {
// return "User [name=" + name + ", age=" + age + "]";
// }
@Override
public int compareTo(User o) {
// TODO Auto-generated method stub
return -1;//逆序存储 全都能存入
//return 1;//顺序存储 全都能存入
//return 0;//只能存入第一个元素 后续都被判断为重复元素
}
}
User u1 = new User("abc",17);
User u2 = new User("ggc",16);
User u3 = new User("xsc",18);
User u4 = new User("abc",22);
User u5 = new User("abc",17);
User u6 = new User("tec",17);
User u7 = u1;
TreeSet<User> tu = new TreeSet<User>();
tu.add(u1);//若User类没有实现比较器接口 则存储失败 报出ClassCastException异常
tu.add(u2);
tu.add(u3);
tu.add(u4);
tu.add(u5);
tu.add(u6);
tu.add(u7);//如果实现的比较器固定返回1 则即使地址相同 属性值相同的两个对象 都能存储
System.out.println(tu.size());
for(User u : tu) {
System.out.println(u);
}
案例:
存储两个值相同的字符串
HashSet 不能存储相同值的String 是因为String类 重写了 hashCode() 和 equals()方法
TreeSet 不能存储相同值的String 是因为String实现了Comparable接口 相同值的String会返回0
//String 已有内部比较器规则 相同的值会返回0
//只有通过外部比较器 覆盖原有比较规则
public class StringCom implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return 1;
}
}
//使用外部比较器 创建TreeSet对象时 将外部比较器对象传入构造方法
TreeSet<String> ts = new TreeSet<String>(new StringCom());
ts.add("abc");
ts.add("abc");
ts.add("abc");
ts.add("abc");
ts.add("dfg");
ts.add("abc");
for(String s : ts) {
System.out.print(s+" ");
}
//abc abc abc abc dfg abc
总结:
有序列表:可以存储重复数据 可以通过Collections使用比较器来排序
无序集合:
String、基本数据类型 都默认去重
HashSet:所有类型都不可排序 自定义类型 通过hashCode() 和equals()去重
TreeSet:所有类型都可通过比较器排序 自定义类型 通过比较器是否返回0去重