一 Set 的特点
无序(没有下标)不重复.主要的分类有HashSet 和 TreeSet
1.HashSet
特点:
(1)底层使用数据结构的hash算法实现的,因此具有很好的存取,查找的性能。
(2)hashSet是线程不安全,所以它相对于线程安全的更快一些。
(3)hashSet值可以为null。
(4) 不存在顺序
1.去重实例public static void fun() {
//创建一个HashSet
//保存 f f a a b b d d
HashSet<String> set =new HashSet<>();
set.add("f");
set.add("f");
set.add("a");
set.add("a");
set.add("b");
set.add("b");
set.add("d");
set.add("d");
//有序:怎么存进去,打印出来就怎样
//迭代器遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String n = iterator.next();
System.out.println(n);
}
// foreach 增强for循环
/*for (String s : set) {
System.out.println(s);
}*/
}
在上面的例子中 打印结果为
a
b
d
f
重合的数据无法被添加进集合,以上是操作系统类,如果对自定义类添加则无法去重需要重写 equals 和 hashcode
例:
先创建一个person类
public class Person {
private String name;
private int age;
public Person() {
super();
// TODO Auto-generated constructor stub
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public static void fun2() {
//创建Set 集合 保存6个人 两两重复
HashSet<Person> pSet = new HashSet<>();
pSet.add(new Person("峰", 12));
pSet.add(new Person("峰", 12));
pSet.add(new Person("鹏", 18));
pSet.add(new Person("鹏", 18));
pSet.add(new Person("坤", 15));
pSet.add(new Person("坤", 15));
pSet.add(new Person("宝", 12));
pSet.add(new Person("海", 18));
for (Person person : pSet) {
System.out.println(person);
}
}
此时打印结果是
Person [name=峰, age=12]
Person [name=坤, age=15]
Person [name=宝, age=12]
Person [name=海, age=18]
Person [name=鹏, age=18]
Person [name=鹏, age=18]
Person [name=坤, age=15]
Person [name=峰, age=12]
此时并没有去重,只要创建一个对象 就会给每一个对象 分配一个新的HashCode码.当添加到set中的对象 HashCode 不相同时 ,没有调用equals 方法,并且对象直接存入Set集合中.
所以要在person类中重写HashCode 和 equals 方法
@Override
public int hashCode() {
// 复杂度 ,是为了减少HashCode码的重复,提高去重时调用equals方法的效率
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;
Person other = (Person) 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;
}
当 添加值:
重写equals 和 hashcode equals相同时,hashcode一定相同,equals不同时,hashcode不一定不同 存在以下几种情况:
(1)当equals 和 hashcode相同时候,set就会认为这是一个重复的对象,不会被add进去;
(2)当equals 相同 hashcode不同的时候,就会在链表分别不同两个bocket;
(3) 当equals 不同 hashcode相同的时候,就会在链表中的一个位置上面保存了两个对象,这样获取其中一个值性能就会降低了;
所以在add对一个对象的添加要重写equals和hashcode,保证对象不重复性。
LinkedHashSet
public static void fun3() {
//Linked 有序 (怎么存的 打印出来还是那个顺序)
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("f");
set.add("f");
set.add("a");
set.add("a");
set.add("b");
set.add("b");
set.add("d");
set.add("d");
for (String string : set) {
System.out.println(string);
}
}
f
a
b
d
Linked 表示有序(怎么存的 打印出来还是那个顺序)
要求:利用set集合a , b, c, d 去除ArrayList集合中的重复元素(操作原ArrayList)
ArrayList 保存了 a a b b c c d d ;
HashSet<String> set = new HashSet<>();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("c");
list.add("d");
list.add("d");
set.addAll(list);
list.clear();
list.addAll(set);
System.out.println(list);
}
2.TreeSet
1.
特点:
(1) 底层使用数据结构二叉树算法进行维护的,因此它的性能相对hashset来说,更差一些,因为它内部会自动进行排序操作。
(2)TreeSet也是线程不安全
(3)排序分为自然排序和定制排序,自然排序是treeset内部会对add进来的值进行自动排序,定制排序可以对排序的条件进行限制。
排序步骤:
1.实现CompareTo 接口
2.重写接口中的compareTo()方法
3.编写你想要的排序规则
public static void fun1() {
//创建一个TreeSet 添加几个数 查看效果
TreeSet<Integer> num = new TreeSet<>();
num.add(1);
num.add(8);
num.add(2);
num.add(5);
num.add(9);
num.add(4);
num.add(4);
num.add(4);
for (Integer integer : num) {
System.out.println(integer);
}
}
1
2
4
5
8
9
集合会调用compareTo(Object obj) 来进行大小的比较,可以对值的大小比较,也可以对对象是否相同来进行比较。如果返回0的时候是相等,非0的时候是不相等。
TreeSet 在存储的时候 只跟compareCode 返回值有关
实现 comparable 中的方法.
返回0 的时候 只有一个元素
返回 正数 打印数据 正序输出
返回 负数 打印数据 到序输出
二叉树
存储规则
比我小的数 放到我的左边(返回负数的情况)
比我大的数 放到我的右边(返回正数的情况)
返回 0 就不存储
输出规则
以升序输出(打印)
定制排序:
TreeSet内部默认是以升序进行排序,而如果要以降序进行排序的时候可以通过comparator来进行实现,可以通过该接口下面的compare(Object o1,Object o2)来进行比较.
实现方式:
可以通过comparator来进行实现,可以通过该接口下面的compare(Object o1,Object o2)来进行比较 例如:
public static void fun2() {
// 创建一个集合TreeSet 添加4个人 (同样利用上面的person类)
//按年龄排序
TreeSet<Person> persons = new TreeSet<>();
persons.add(new Person("海", 15));
persons.add(new Person("蛋", 11));
persons.add(new Person("斌", 10));
persons.add(new Person("瑞", 16));
persons.add(new Person("豆", 15));
persons.add(new Person("剩", 14));
for (Person person : persons) {
System.out.println(person);
}
}
需要在person类中重写 int compareTo 来实现
public int compareTo(Person o) {
/* //按年龄
int num = this.age - o.getAge();
return num;*/
//按名字
// return this.name.compareTo(o.getName());
//注要按年龄排 次要按姓名
/*if (this.age!=o.getAge()) {
return this.age - o.getAge();
}else {
return this.name.compareTo(o.getName());
}*/
int num = this.age - o.getAge();
return num!=0?this.age - o.getAge():this.name.compareTo(o.getName());
}
对于系统类的排序,由于系统类本身具有内置的排序规则,所以如果要自定义排序规则,需要创建比较器重写比较规则.
例:
public static void fun3() {
//集合中保存字符串 按字符串长度排序
//利用TreeSet 的构造方法 直接将比较器的实现类传进去.
TreeSet<String> set = new TreeSet<>(new compareImpl());
set.add("asfgddfb");
set.add("asfgddfb");
set.add("as");
set.add("asfb");
set.add("asfgdfb");
set.add("addfbfghjgfmhyku");
System.out.println(set);
}
比较器:
class compareImpl implements Comparator<String>{
// 实现比较器方法
@Override
public int compare(String o1, String o2) {
int length = o1.length()-o2.length();
int num = o1.compareTo(o2);
int rel = length==0?num:length;
return rel==0?-1:rel;
}
}
打印结果为:
[as, asfb, asfgdfb, asfgddfb, asfgddfb, addfbfghjgfmhyku]
要保证数据完整,一般但返回值为零时,建议返回一个不为零的值.