9.Set集合

目录

9.Set集合

9.1.Set集合

9.1.1.Set集合遍历方式

9.1.2.Set集合特点

9.1.3.Set没有对父接口Collection进行功能上扩充

9.1.4.Set存储元素为什么不重复

9.2.Set接口实现类_HashSet

9.2.1.HashSet存储结构

9.2.2.HashSet特点

9.2.3.HashSet没有对父接口Set进行功能上补充

9.2.4.HashSet存储自定义类型元素必须重写哪两个方法,为什么

9.3.哈希值HashCode

9.4.哈希表

9.4.1.jdk8前后哈希表区别

9.4.2.哈希表特点,为什么查询速度快(哈希表原理)

9.5.Set接口实现类_LinkedHashSet

9.5.1.LinkedHashSet存储结构、存储元素特点、与HashSet区别

 


 


9.Set集合


9.1.Set集合

9.1.1.Set集合遍历方式

9.1.2.Set集合特点

9.1.3.Set没有对父接口Collection进行功能上扩充

9.1.4.Set存储元素为什么不重复

1.java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中方法基本一致。并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
2.set集合有多个子类,主要介绍其中java.util.HashSet和java.utill.LinkedHashSet两个集合。
3.Set集合取出元素的方式迭代器,增强for;
4.Set接口特点
     1.不允许存储重复元素,子类集合既有有序也有无序的。
     2.没有索引,没有带索引的方法,也不能使用普通for循环遍历
5.java.util.Set接口 extends Collection接口
  Java.util.HashSet集合 implements Set接口
6.Set接口中的方法和Collection接口中方法一致,不再学习,下面主要学习Set接口的子类(实现类)

/**
 * Set集合不允许重复元素的原理:
 */
public class Demo3_HashSet {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<String> set = new HashSet<>();
        String s1 = new String("abc");
        String s2 = new String("abc");
        set.add(s1);
        set.add(s2);
        set.add("重地");
        set.add("通话");
        set.add("abc");
        System.out.println(set);//不重复[重地, 通话, abc]
    }
}

1.当new HashSet<>()时在堆内存开辟空间创建集合,HashSet底层是哈希表:数组+链表/红黑树。哈希表横向是数组结构,竖向是链表/红黑树结构。
2.set集合在调用add方法存储元素时会调用元素的hashCode方法和equals方法判断元素是否重复set.add(s1):add方法会调用s1的hashCode方法,计算字符串"abc"的哈希值为96354,然后在集合中找有没有96354这个哈希值的元素,若没有则将s1存储到集合中。
3.set.add(s2):add方法会调用s2的hashCode方法,计算字符串"abc"的哈希值为96354,然后在集合中找有没有96354这个哈希值的元素,发现有(哈希冲突),s2会调用equals方法和哈希值相同的元素进行比较即s2.equals(s1),若两个元素的哈希值相同并且equals方法返回true,则认定这两个元素相同,就不会把s2存储到集合中。
4.set.add("重地"):add方法会调用“重地”的hashCode方法,计算字符串“重地”哈希值1179395,然后在集合中找有没有1179395这个哈希值元素,若没有就会把"重地"字符串存储到集合中。
5.set.add("通话"):add方法会调用“通话”的hashCode方法,计算字符串“通话”的哈希值为1179395,然后在集合中找有没有1179395这个哈希值的元素,发现有(哈希冲突),“通话”会调用equals方法和哈希值相同的元素进行比较即“通话”.equals("重地"),若两个元素的哈希值相同但equals方法返回 false,则认定这两个元素不相同,就会把"通话"存储到集合中。

9.2.Set接口实现类_HashSet

9.2.1.HashSet存储结构

9.2.2.HashSet特点

9.2.3.HashSet没有对父接口Set进行功能上补充

9.2.4.HashSet存储自定义类型元素必须重写哪两个方法,为什么

1.HashSet特点:
 *      1.存储不重复元素
 *      2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
 *      3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
 *      4.底层是一个HashMap哈希表结构(特点是查询速度非常快)
 *      5.因为HashSet中没有除Set中方法外的特有方法,所以可以使用多态创建集合对象。因为多态弊端就是看不到子类的特有方法

  public class Demo1_HashSet {
    public static void main(String[] args) {
        //使用多态创建HashSet集合对象
        Set<Integer>set=new HashSet<>();
        //使用add方法向集合添加元素
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1);
        //使用迭代器(增强for)遍历set集合
        Iterator<Integer> it = set.iterator();
        while(it.hasNext()){
            Integer next = it.next();
            System.out.println(next);//1,2,3(无序,不重复)
        }
    }
}

 

2.HashSet存储自定义类型元素:

给HashSet中存放自定义类型的元素时,必须重写对象中hashCode方法和equals方法,建立自己的比较方式才能保证HashSet集合中的对象唯一(元素不重复)。之前使用hashSet存储字符串,Integer等类型元素,他们都是java已经定义好的类,这些类都已经重写了hashCode和equals方法。因此我们使用hashSet存储自定义类型元素,必须重写这两个方法才能保证元素 的唯一性。


 * HashSet存储自定义类型元素:
 * Set集合存储元素唯一(不重复)前提:必须重写hashCode方法和equals方法
 * 要求:同名同年龄的人,视为同一个人,只能存储一次。

public class Demo4_HashSet {
    public static void main(String[] args) {
        //创建HashSet集合存储Person
        HashSet<Demo4_Person> set=new HashSet();
        Demo4_Person p1 = new Demo4_Person("zhangsan",18);
        Demo4_Person p2 = new Demo4_Person("zhangsan", 18);
        Demo4_Person p3 = new Demo4_Person("zhangsan", 19);
        //p1、p2两个对象哈希值不同
        System.out.println(p1.hashCode());//21685669
        System.out.println(p2.hashCode());//2133927002
        //p1、p2两个对象地址值不同(未重写的equals方法比较的是元素的地址,等价==运算符)
        System.out.println(p1==p2);
        System.out.println(p1.equals(p2));
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(set);

       /*显然在没有重写hashCode方法和equals方法时,p1,p2虽然内容重复但
        * 还是被写进set集合了,违反了set集合的唯一性。hashCode获取的是元素哈希值,
          未重写的equals比较的是地址而不是内容,这两个方法未重写导致比较出来的结果都是false, 
          所以当元素哈希值不同但内容相同时也会被存入集合。
        * [Demo4_Person{name='zhangsan', age=19},
        * Demo4_Person{name='zhangsan', age=18},
        * Demo4_Person{name='zhangsan', age=18}]
        */


        /**
         * 下面使用Demo4_Person1创建对象,该类重写了hashCode和equals方法
         */
        HashSet<Demo4_Person1> set1 = new HashSet<>();
        Demo4_Person1 q1 = new Demo4_Person1("zhangsan",18);
        Demo4_Person1 q2 = new Demo4_Person1("zhangsan", 18);
        Demo4_Person1 q3 = new Demo4_Person1("zhangsan", 19);
        set1.add(q1); //调用add时会调用hashCode和equals方法进行比较是否重复
        set1.add(q2);
        set1.add(q3);
        System.out.println(q1.hashCode());
        System.out.println(q2.hashCode());
        System.out.println(q1==q2);//比较的是地址
        System.out.println(q1.equals(q2));//重写equals后比较内容
        System.out.println(set1);
    }
}

 

9.3.哈希值HashCode

 * 1.哈希值
 *     哈希值是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)。
 * 2.Object类中有一个方法hashCode()可以获取对象的哈希值
 *     int hashCode():返回该对象的哈希值
 *     hashCode()方法的源码:public native int hashCode(); native:代表该方法调用的是本地操作系统的方法。
 

9.4.哈希表

9.4.1.jdk8前后哈希表区别

9.4.2.哈希表特点,为什么查询速度快(哈希表原理)

1.HashSet集合存储数据的结构是:哈希表
2.jdk1.8版本之前与之后,哈希表的区别(或jdk1.8与jdk1.9中哈希表区别)
        1.8版本之前:哈希表=数组+链表
        1.8版本之后:哈希表=数组+链表+红黑树(提高查询速度,折半查找,一次排除一半)
3.哈希表特点:查询速度快
4.哈希表的原理:
      1.数组结构:把元素进行了分组,相同哈希值的元素是一组
      2.链表/红黑树结构:把相同哈希值的元素连接到一起
      3.数组结构初始容量是16,即0-15,当向集合中存储数据时,先计算元素的哈希值(即在数组中存储的位置),比如元素“abc”的哈希值96354,即为这个元素在数组中存储位置。将哈希值存储在数组中,将元素挂到这个索引的下面。元素"重地"的哈希值和"通话"的哈希值都是1179395,称为哈希冲突,将这两个元素都挂到数组中值为1179395的索引下面。这就是为什么哈希表查询非常快,它先是把数据根据哈希值相同进行分组,然后通过链表或红黑树将相同哈希值的元素连接到一起。
      4.jdk1.9开始,当链表中的节点元素超过8个,就会将链表结构转化为红黑树结构(红黑树查询非常快)目的是提高查询速度。

9.5.Set接口实现类_LinkedHashSet

9.5.1.LinkedHashSet存储结构、存储元素特点、与HashSet区别

1.在java.util包中找到set接口,set接口的一个实现类HashSet类下有一个子类LinkedHashSet,也就是LinkedHashSet继承了HashSet并实现了Set接口
2.java.util.LinkedHashSet集合 extends HashSet集合
3.LinkedHashSet集合特点:
 *    底层是一个哈希表(数组+链表/红黑树)+链表多了一条链表(记录元素的存储顺序),保证元素有序.因此它与HashSet集合的区别就在于前者是有序的,后者是无序的,
 *    其它特点相同即元素不能重复元素没有索引

public class Demo5_LinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("www");
        set.add("abc");
        set.add("abc");
        set.add("itcast");
        System.out.println(set);//[abc, www, itcast]无序,不允许重复

        LinkedHashSet<String> linked = new LinkedHashSet<>();
        linked.add("www");
        linked.add("abc");
        linked.add("abc");
        linked.add("itcast");
        System.out.println(linked);//[www, abc, itcast]有序,不允许重复
    }
}

9.6.TreeSet

HashSet是散列存储,TreeSet是二叉树有序存储,这个有序不是存储有序而是根据排序规则进行排序,自定义元素需要实现comparable接口重写compartor方法定义自己排序规则。

排序说明

如果现在要是想进行排序的话,则必须在 Person 类中实现 Comparable 接口。

public class Person implements Comparable<Person> {
    private String name;
    private int age;
    public int compareTo(Person per) {
        if (this.age > per.age) {
            return 1;
        } else if (this.age < per.age) {
            return -1;
        } else {
            return 0;
        }
    }
    public Person() {}
    public Person(String name, int age) {
        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;
    }
    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}


public class TreeSetPersonDemo01 {
    public static void main(String[] args) {
    Set<Person> all = new TreeSet<Person>();
    all.add(new Person("张三", 10));
    all.add(new Person("李四", 10));
    all.add(new Person("王五", 11));
    all.add(new Person("赵六", 12));
    all.add(new Person("孙七", 13));
    System.out.println(all);
    }
}
  • [姓名:张三,年龄:10, 姓名:王五,年龄:11, 姓名:赵六,年龄:12, 姓名:孙七,年龄:13]
  • 从以上的结果中可以发现,李四没有了。因为李四的年龄和张三的年龄是一样的,所以会被认为是同一个对象。则 此时必须修改 Person 类,如果假设年龄相等的话,按字符串进行排序。
public int compareTo(Person per) {
    if (this.age > per.age) {
        return 1;
    } else if (this.age < per.age) {
        return -1;
    } else {
        return this.name.compareTo(per.name);
    }
}

小结

  • 关于 TreeSet 的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现 Comparable 接口,则不能实现 TreeSet 的排序,会报类型转换(转向 Comparable 接口)错误。
  • 换句话说要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。 不过 TreeSet 的集合因为借用了 Comparable 接口,同时可以去除重复值,而 HashSet 虽然是 Set 接口子类,但是对于没有复写 Object 的 equals 和 hashCode 方法的对象,加入了 HashSet 集合中也是不能去掉重复值的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值