第十八天(Set,HashSet,哈希表,LinkedHashSet,TreeSet,应用场景)

Set系列集合

无序:存取顺序不一致
不重复:可以去除重复
五索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Set集合的实现类

HashSet:无序、不重复、无索引
LinkedHashSet:有序、不重复、无索引
TreeSet:可排序、不重复、无索引

Set接口中的方法基本上与Collection的API一致。

public static void main(String[] args) {

        //创建一个Set集合的对象
        Set<String> s   = new HashSet<>();

        //2.添加元素
        //如果当前元素是第一次添加,那么可以添加成功,返回true
        //如果当前元素第二次添加,那么添加失败,返回false
        boolean r1 = s.add("张三");
        boolean r2 = s.add("李四");
        boolean r3 = s.add("王五");
        //打印集合
        //迭代器遍历
        Iterator<String> it = s.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        //增强for遍历
        for (String s1 : s) {
            System.out.println(s1);
        }
        //Lambda表达式遍历
        s.forEach(str-> System.out.println(str));
    }

HashSet底层原理

HashSet集合底层采取哈希表储存数据
哈希表是一种对于增删改查数据性能都比较好的结构

哈希表组成

JDK8之前:数组+链表

JDK8之后:数组+链表+红黑树

哈希值

对象的整数表现形式

  1. 根据hashCode方法算出来的int类型的整数
  2. 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算。
  3. 一般情况下,会重写hashCode方法 ,利用对象内部属性值计算哈希值 。

对象的哈希值特点

如果没有重写hashCode方法,不同对象计算出的哈希值是不同的。
如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的。
在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)

    public static void main(String[] args) {
        //1.创建对象
        Student s1 = new Student("张三", 23);
        Student s2 = new Student("张三", 23);
        //2.如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
        //如果已经重写hashCode方法,不同对象只要属性值相同,计算出的和哈希值就是一样的
        System.out.println(s1.hashCode());//24022543
        System.out.println(s2.hashCode());//24022543
        //再在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354
    }

HashSetJDK8以前底层原理

  1. 创建一个默认长度16,默认加载因子0.75的数组,数组名table
    HashSet hm = new HashSet<>();
  2. 根据元素的哈希值跟数组的长度计算出应存入的位置
    int index = (数组长度 - 1)&哈希值;
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果位置不为null,表示有元素,则调用equals方法比较属性值
    1. 一样:不存 不一样:存入数组,形成链表

JDK8以前:新元素存入数组,老元素存在新元素下面

JDK8以后:新元素直接挂在老元素下面

JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树。

如果集合中储存的时自定义对象,必须要重写hashCode和equals方法.

HashSet三个问题

hashset为什么存和取的顺序不一样

存的位置是计算出来的,包含链表,存和取的顺序不一样

hashset为什么没有索引

因为hashset包含数组,链表,红黑树三个组合而成.

hashset是利用什么机制去重的呢

通过hsahcode方法确定位置,通过equals方法判断是否一样.

LinkedHashSet底层原理

有序,不重复,无所引.

这里的有序指的是保证存储和取出的元素顺序一致

原理:底层数据结构是依然哈希表,只是每个元素有额外的多了一个双链表的机制记录储存的顺序.

public static void main(String[] args) {
        //创建三个学生对象
        Student s1 = new Student("zhangsan", 23);
        Student s2 = new Student("lisi", 22);
        Student s3 = new Student("wangwu", 33);
        Student s4 = new Student("wangwu", 33);
        //创建集合用来添加学生
        LinkedHashSet<Student> hs = new LinkedHashSet<>();
        //添加元素
        System.out.println(hs.add(s1));//true
        System.out.println(hs.add(s2));//true
        System.out.println(hs.add(s3));//true
        System.out.println(hs.add(s4));//false
        //打印集合
        System.out.println(hs);//[Student{name = zhangsan, age = 23}, Student{name = lisi, age = 22}, Student{name = wangwu, age = 33}]
          }
  1. LinkedHashSet集合的特点和原理是怎样的?
    有序,不重复,无所引
    底层基于哈希表,使用双链表记录添加顺序
  2. 在以后如果数据要去重,我们使用哪个?
    默认使用HashSet
    如果要求去重且存取有序,才使用LinkedHashSet.

TreeSet

特点

不重复,无所引,可排序

可排序:按照元素的默认规则(由大到小)排序.

TreeSet集合底层是基于红黑树的数据结构实现排序的,怎删改查性能都较好.

  public static void main(String[] args) {
        //创建treeset集合对象
        TreeSet<Integer> ts = new TreeSet<>();
        //添加元素
        ts.add(1);
        ts.add(6);
        ts.add(5);
        ts.add(2);
        ts.add(3);
        System.out.println(ts);//[1, 2, 3, 5, 6]
    }

TreeSet集合默认的规则

对于数值类型:integer,Double,默认按照从小到大的顺序进行排序.

对于字符,字符串类型:按照字符再Ascii码表中的数字升序进行排序,

TreeSet的两种比较方式

方式一:默认排序/自然排序:javabean类实现Comparable接口指定比较规则
this:表示当前要添加的元素
O:表示已经在红黑树存在的元素
返回值:
负数:认为要添加的元素是小的,存左边.
正数:认为要添加的元素是大的,村右边.
0:认为要添加的元素已经存在,舍弃

public class Student implements Comparable<Student>{
//this:表示当前要添加的元素
//O:表示已经在红黑树存在的元素
 //返回值:
    //负数:认为要添加的元素是小的,存左边.
    //正数:认为要添加的元素是大的,村右边.
    //0:认为要添加的元素已经存在,舍弃
 @Override
    public int compareTo(Student o) {
        //根据年龄排序,年龄相同视为同一同学
        int i = this.getAge() - o.getAge();
        return i;
}
 public static void main(String[] args) {
        //创建三个学生对象
        Student s1 = new Student("zhangsan", 32);
        Student s2 = new Student("lisi", 23);
        Student s3 = new Student("wangwyu", 23);

        //创建treeset集合对象
       TreeSet<Student> ts = new TreeSet<>();
       //添加学生对象
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        System.out.println(ts);//按照年龄大小排序
    }

方式二:比较器排序

​ 创建TreeSet对象的时候,传递比较器Comparator指定规则

public static void main(String[] args) {
    //创建集合
    /*TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            //按照长度排序
            int i = o1.length() - o2.length();
            //如果长度一样则按照首字母排序
             i = i == 0 ? o1.compareTo(o2) : i;
            return i;
        }
    });*/
    TreeSet<String> ts = new TreeSet<>(o1,o2) {
            //按照长度排序
            int i = o1.length() - o2.length();
            //如果长度一样则按照首字母排序
             i = i == 0 ? o1.compareTo(o2) : i;
            return i;
        }
    });
    //添加元素
    ts.add("c");
    ts.add("ab");
    ts.add("df");
    ts.add("qwer");
    System.out.println(ts);//[c, ab, df, qwer]
}

默认用第一种,如果第一种不能满足当前需求,就使用第二种.

方式一方式二同时存在的时候,使用的是方式二.

总结

  1. 如果想要集合中的元素可重复
    用ArrayList集合,基于数组的.(用的最多 )
  2. 如果想对集合中的元素去重
    用HashSet集合,基于哈希表的.(用的最多)
  3. 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
    用LinkedList集合,基于链表的
  4. 如果想对集合中的元素去重,而且保证存取顺序
    用LinkedHashSet集合,基于哈希表和双链表 ,效率低于HashSet.
  5. 如果想要对集合中的元素进行排序
    用TreeSet集合,基于红黑数.后续也可以用List集合实现排序
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值