Java中的单列集合体系结构(三)

Set系列集合

1、无序:存取顺序不一致

2、不重复:可以去除重复

3、无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Set集合的实现类

1,HashSet:无序、不重复、无索引

2、LinkedHashSet:有序、不重复、无索引

3、TreeSet:可排序、不重复、无索引

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

//创建Set实现类集合的对象
Set<String> s=new HashSet<>();

s.add("张三");
boolean r2=s.add("张三");

System.out.println(r2);
System.out.println(s);
        
//false
//不能重复
//[张三]

s.add("李四");
s.add("王五");

//[李四, 张三, 王五]
//存入和输出的顺序不一定相同

//迭代器遍历
Iterator<String> it=s.iterator();
while(it.hasNext()){
    String cd=it.next();
    System.out.println(cd);
}

//增强for遍历
for (String s1 : s) {
     System.out.println(s);
}

//匿名内部类
s.forEach(new Consumer<String>() {
     @Override
     public void accept(String str) {
          System.out.println(str);
     }
});

//简化Lambda表达式
s.forEach(str-> System.out.println(str));


HashSet底层原理

1、HashSet集合底层采取哈希表存储数据

2、哈希表是一种对于增删改查数据性能都较好的结构

哈希表组成

1、JDK8之前:数组+链表

2、JDK8开始:数组+链表+红黑树

哈希值:对象的整数表现形式

1、根据hashCode方法算出来的int类型的整数

2、该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算

3、一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值的特点

1、如果没有重写hashCode方法,不同对象计算出的哈希值是不同的

2、如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的

3、在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)

//创建学生类
public class Student {
    private String name;
    private int age;

    //重写hashCode方法
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public Student() {
    }

    public Student(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 "Student{name = " + name + ", age = " + age + "}";
    }
}


//创建Test类
public class Test {
    public static void main(String[] args) {
        //创建对象
        Student stu1=new Student("zhangsan",23);
        Student stu2=new Student("zhangsan",23);

        System.out.println(stu1.hashCode());
        System.out.println(stu2.hashCode());

        //重写hashCode前
        //990368553
        //1096979270


        //重写hashCode后
        //-1461067292
        //-1461067292

        //哈希碰撞
        System.out.println("abc".hashCode());
        System.out.println("acD".hashCode());
        
        //96354
        //96354



    }
}





 HashSet 的底层原理

1、创建一个默认长度为16,默认加载因子为0.75的数组,数组名为table

2、根据元素的哈希值跟数组的长度计算处应存入的位置

int index = (数组长度-1) & 哈希值

3、判断当前位置是否为null,如果是null直接存入

4、如果位置不为null,表示有元素,则调用equals方法比较属性值

5、属性值一样:不存        属性值不一样:存入数组,形成链表

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

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

注:

1、当数组中存储的元素>16(数组长度)*0.75(加载因子)时,数组成原来的扩容两倍

2、当链表长度大于8而且数组长度大于等于64时,当前链表会转成红黑树以加快访问速度

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

LinkedHashSet的底层原理

1、有序、不重复、无索引

有序是指存储和取出的元素顺序一致

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

TreeSet的特点

1、不重复、无索引、可排序

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

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

public class TreeSetDemo {
    public static void main(String[] args) {

        TreeSet<Integer> ts=new TreeSet<>();

        ts.add(1);
        ts.add(2);
        ts.add(4);
        ts.add(5);
        ts.add(3);

        System.out.println(ts);

        //[1, 2, 3, 4, 5]

        //迭代器
        Iterator<Integer> it=ts.iterator();
        while(it.hasNext()){
            Integer i=it.next();
            System.out.print(i+" ");
        }
        System.out.println();


        //增强for
        for (Integer t : ts) {
            System.out.print(t+" ");
        }
        System.out.println();


        //Lambda
        ts.forEach(in-> System.out.print(in+" "));
        
        //1 2 3 4 5 
        //1 2 3 4 5 
        //1 2 3 4 5 
    }
}

TreeSet集合默认的规则

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

2、对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序

TreeSet的两种比较方式:

1、默认排序/自然排序:javabean类实现Comparable接口指定比较规则

//创建学生类
public class Student implements Comparable<Student>{
    private String name;
    private int age;

    //重写compareTo,按照成绩升序排序
    //this表示当前要添加的元素
    //o表示已存在在红黑树中的元素
    //返回值:负数:认为要添加的元素是小的,存左边
    //正数:认为要添加的元素是大的,存右边
    //0:认为要添加的已经存在,舍弃
    @Override
    public int compareTo(Student o) {
        return this.getAge()-o.getAge();
    }

    public Student() {
    }

    public Student(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 "Student{name = " + name + ", age = " + age + "}";
    }

}

//实现类
public class Test {
    public static void main(String[] args) {
        //创建对象
        Student stu1=new Student("zhangsan",24);
        Student stu2=new Student("lisi",23);
        Student stu3=new Student("wangwu",25);

        TreeSet<Student> ts = new TreeSet<>();

        ts.add(stu1);
        ts.add(stu2);
        ts.add(stu3);

        System.out.println(ts);

        //[Student{name = lisi, age = 23}, Student{name = zhangsan, age = 24},     
        //Student{name = wangwu, age = 25}]
        
    }
}

 2、比较器排序:创建TreeSet对象的时候,传递比较器Comparator制定规则

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

public class TreeSetDemo2 {
    public static void main(String[] args) {

        /*
         * 按照长度排序,如果长度一样则按照首字母排序
         */


        //创建集合
        //o1:表示当前要添加的元素
        //o2:表示已经在红黑树存在的元素
        //返回值:负数:认为要添加的元素是小的,存左边
        //正数:认为要添加的元素是大的,存右边
        //0:认为要添加的已经存在,舍弃
        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、如果想要集合中的元素可重复,而且当前的增删操作明显多于查询:

        用LinkedList集合,基于链表的。

3、如果相对集合中的元素去重:

        用HashSet集合,基于哈希表的。

4、如果想对集合中的元素去重,可且保证存取顺序:

        用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet。

5、如果想对集合中的元素进行排序:

        用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值