集合框架(3):set | HashSet | LinkedHashSet | TreeSet的底层源码


文章链接
Java语法https://blog.csdn.net/weixin_45606067/article/details/107049186
一维数组与二维数组、内存解析https://blog.csdn.net/weixin_45606067/article/details/107049178
面向对象(1/3)类和对象https://blog.csdn.net/weixin_45606067/article/details/108234276
面向对象(2/3)封装性、继承性、多态性https://blog.csdn.net/weixin_45606067/article/details/108234328
面向对象(3/3)抽象类、接口、内部类、代码块https://blog.csdn.net/weixin_45606067/article/details/108258152
异常处理待更新
多线程(1/2)https://blog.csdn.net/weixin_45606067/article/details/107067785
多线程(2/2)https://blog.csdn.net/weixin_45606067/article/details/107067857
常用类https://blog.csdn.net/weixin_45606067/article/details/108283203
枚举与注解待更新
集合(1/5)Collection、Iterator、增强forhttps://blog.csdn.net/weixin_45606067/article/details/107046876
集合(2/5)List、ArrayList、LinkedList、Vector的底层源码https://blog.csdn.net/weixin_45606067/article/details/107069742
集合(3/5)set、HashSet、LinkedHashSet、TreeSet的底层源码
集合(4/5)Map、HashMap底层原理分析https://blog.csdn.net/weixin_45606067/article/details/107042949
集合(5/5)LinkHashMap、TreeMap、Properties、Collections工具类https://blog.csdn.net/weixin_45606067/article/details/107069691
泛型与Filehttps://blog.csdn.net/weixin_45606067/article/details/107124099
IO流与网络编程https://blog.csdn.net/weixin_45606067/article/details/107143670
反射机制待更新
Java8新特性https://blog.csdn.net/weixin_45606067/article/details/107280823
Java9/10/11新特性待更新

一、Set

1.Set接口的框架结构:存储无顺序的,不可重复的数据。

  • HashSet:作为set接口的主要实现类,线程不安全,可以存储null值。底层为数组+链表。
  • LinkedHashSet:作为HashSet的子类。遍历其内部数据时,可以按照添加的顺序遍历。
    对于频繁遍历的操作,LinkedHashSet的效率高于HashSet。
  • TreeSet:采用红黑树的存储结构,可以按照添加对象的指定属性,进行排序。

2.如何理解Set的无序,不可重复。
以HashSet为例说明:
①无序性:不等于随机性。
存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值添加。
②不可重复性:保证添加的元素按照equals()方法判断时,不能返回true。即,相同的元素只能添加一个。

3.Set接口中没有额外定义新的方法,使用的都是Collection接口中声明过的方法。

4.添加元素的过程:以HashSet为例:

我们像HashSet中添加元素a,首先调用元素a所在类的hashcode方法,计算a的哈希值,
  此哈希值通过某种算法计算中在HashSet底层数组的存放位置(即:索引位置),
  判断数组此位置是否已经有元素:
      如果此位置上没有其他元素,则元素a直接添加成功。---情况1
      如果此位置有其他元素b(或以链表形式存在多个元素),则比较a和b的哈希值:
          如果哈希值不相同,则元素a添加成功。  ---情况2
          如果哈希值相同,则调用元素a所在类的equals()方法:
              equals()返回true,元素添加失败。
              equals()返回返回false,则元素a添加成功。 ---情况3

说明:对于添加成功的情况2和情况3而言:元素a与已经存在指定位置上数据以链表方式存。
jdk7中:元素a放到数组中,指向原来的元素。
jdk8中原来的元素放在数组中,指向a元素。

5.要求:
①向Set中添加的数据,其所在的类一定要重写hashCode()和equals()方法
②重写hashCode()和equals()方法尽可能保持一致:相等的对象必须具有相等的哈希值。

练习题:在list内去除重复数据值。

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("123");
    list.add("456");
    list.add("789");
    list.add("456");
    List list2=duplicateList(list);
    Iterator<String> iterator = list2.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next ());
    }
}
public static List<String> duplicateList(List list){
    HashSet<String> set = new HashSet<>();
    set.addAll(list);
    return new ArrayList(set);
}

二、HashSet

添加元素的过程:以HashSet为例:

  我们像HashSet中添加元素a,首先调用元素a所在类的hashcode方法,计算a的哈希值,
  此哈希值通过某种算法计算中在HashSet底层数组的存放位置(即:索引位置),
  判断数组此位置是否已经有元素:
      如果此位置上没有其他元素,则元素a直接添加成功。---情况1
      如果此位置有其他元素b(或以链表形式存在多个元素),则比较a和b的哈希值:
          如果哈希值不相同,则元素a添加成功。  ---情况2
          如果哈希值相同,则调用元素a所在类的equals()方法:
              equals()返回true,元素添加失败。
              equals()返回返回false,则元素a添加成功。 ---情况3

三、LinkHashSet

1.LinkedHashSet的使用:
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了一对双向链表,记录此数据的前一个数据和后一个数据。

2.优点:
对于频繁遍历的操作,LinkedHashSet的效率高于HashSet。

四、TreeSet

1.自然排序

1.向TreeSet中添加的数据,要求是同一个类的对象,不能添加不同类的对象。
2.两种排序方式:自然排序(实现Comparable接口)和定制排序(实现Comparator接口)
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()

代码实现:

@Test
    public void test1(){
        TreeSet set = new TreeSet();
        //举例1:
        set.add(34);
        set.add(-25);
        set.add(77);
        set.add(8);
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        System.out.println("*************举例2*****************");
        TreeSet set1 = new TreeSet();
        set1.add(new User("Tom",22));
        set1.add(new User("Jerry",24));
        set1.add(new User("BeiBei",21));
        set1.add(new User("DongDong",20));
        set1.add(new User("MM",53));
        set1.add(new User("MM",18));
        Iterator iterator1 = set1.iterator();
        while (iterator1.hasNext()){
            System.out.println(iterator1.next());
        }
    }
}

public class User implements Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    //按照姓名从大到小排序,年龄从小到大排序,
    @Override
    public int compareTo(Object o) {
        if (o instanceof User){
            User user= (User) o;
            int num= this.name.compareTo(user.name);
            if (num!=0){
                return -num;
            }else{
                return Integer.compare(this.age,user.age);
            }
        }else{
            throw new RuntimeException("类型不一致!");
        }
    }
}

2.定制排序

定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。

代码实现:

@Test
public void test2(){
  Comparator comparator = new Comparator(){
    //按照年龄从小到大排序
    @Override
    public int compare(Object o1, Object o2) {
      if (o1 instanceof User && o2 instanceof User){
        User d1= (User) o1;
        User d2= (User) o2;
        return Integer.compare(d1.getAge(),d2.getAge());
      }else{
        throw new RuntimeException("类型不一致!");
      }
    }
  };
  TreeSet set = new TreeSet(comparator);
  
  set.add(new User("Tom",22));
  set.add(new User("Jerry",24));
  set.add(new User("BeiBei",21));
  set.add(new User("DongDong",20));
  set.add(new User("Mary",18));
  set.add(new User("MM",18));
  Iterator<Object> iterator = set.iterator();
  while (iterator.hasNext()){
    System.out.println(iterator.next());
  }
}

如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值