一 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]要保证数据完整,一般但返回值为零时,建议返回一个不为零的值.