Java Collection接口的子接口之Set接口及其Set接口的主要实现类HashSet,LinkedHashSet,TreeSet详解(二)

简介: Java Collection接口的子接口之Set接口及其Set接口的主要实现类HashSet,LinkedHashSet,TreeSet详解

HashSet的课堂练习题如下

具体代码如下

public class HashSetExercise {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("tom", 20));
        hashSet.add(new Employee("jack", 19));
        hashSet.add(new Employee("tom", 20));
        System.out.println(hashSet);
    }
}
class Employee {
    private String name;
    private int age;
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
//如果name和age相同,则返回相同的hash值
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Objects.equals(name, employee.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

输出结果如下

[Employee{name='tom', age=20}, Employee{name='jack', age=19}]

六、LinkedHashSet的使用

1、LinkedHashSet的全面说明

2、LinkedHashSet底层机制示意图

3、LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据

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

@Test
    public void SetTest(){
        Set hashSet = new LinkedHashSet();
        hashSet.add("hello");
        hashSet.add("AA");
        hashSet.add(12);
        hashSet.add(12);
        hashSet.add("cc");
        hashSet.add(new User("小红",13));
        hashSet.add(new User("小红",13));
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

遍历的结果:是按照添加的顺序,进行遍历的。

hello

AA

12

cc

www.entity.User@168edbb

LinkedHashSet底层源码分析

@SuppressWarnings({"all"})
public class LinkedHashSetResource {
    public static void main(String[] args) {
        //分析一下LinkedHashSet的底层机制
        Set set = new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);
        set.add(new Customer("刘", 1001));
        set.add(123);
        set.add("ly");
        System.out.println("set=" + set);
        //1.LinkedHashSet 加入顺序和取出元素/数据的顺序一致
        //2.LinkedHashSet底层维护的是一个LinkedHashMap(是HashMap的子类)
        //3.LinkedHashSet底层结构(数组+双向链表)
        //4.添加第一次时,直接将数组table扩容到16,存放的结点类型是LinkedHashMap$Entry
        //5.数组是HashMap$Node[]存放的元素/数据是LinkedHashMap$Entry类型
        /*
        //继承关系是在内部类完成
        static class Entry<K,V> extends HashMap.Node<K,V> {
            Entry<K,V> before, after;
            Entry(int hash, K key, V value, Node<K,V> next) {
                super(hash, key, value, next);
            }
        }
         */
    }
}
class Customer {
    private String name;
    private int no;
    public Customer(String name, int no) {
        this.name = name;
        this.no = no;
    }
    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", no=" + no +
                '}';
    }
}

输出结果如下

set=[AA, 456, Customer{name='刘', no=1001}, 123, ly]

LinkedHashSet对应的练习题

具体代码如下

public class LinkedHashSetExercise {
    public static void main(String[] args) {
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奥迪", 10000));
        linkedHashSet.add(new Car("保时捷", 1000000));
        linkedHashSet.add(new Car("奥迪", 10000));
        System.out.println("linkedHashSet=" + linkedHashSet);
    }
}
class Car {
    private String name;
    private double price;
    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
    //重写equals方法,和hashCode
    //当name和price相同时,就返回相同的hashCode,equals返回true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 &&
                Objects.equals(name, car.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

输出结果如下

linkedHashSet=[Car{name='奥迪', price=10000.0}, Car{name='保时捷', price=1000000.0}]

TreeSet类的详解

TreeSet最大的特点就是可以排序,具体案例代码如下

public class TreeSet_ {
    public static void main(String[] args) {
        //1.当我们使用无参构造器,创建TreeSet时,仍然是无序的
        //2.希望添加的元素,按照字符串大小来排序
        //3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)
        //并制定排序规则
        //简单看看源码
        //源码解读
        /*
        1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator
        public TreeMap(Comparator<? super K> comparator) {
            this.comparator = comparator;
        }
        2.在调用add方法时treeSet.add("tom");,在底层会执行到
        if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
            do {
                parent = t;
                //动态绑定到我们的匿名内部类(对象)compare
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else //如果相等,即返回0,这个Key就没有加入
                    return t.setValue(value);
            } while (t != null);
        }
         */
//        TreeSet treeSet = new TreeSet();
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //下面调用String的compareTo方法 进行字符串大小比较,从小到大排序
//                return ((String) o1).compareTo((String) o2);
                //下面调用String的compareTo方法 进行字符串大小比较,从大到小排序
//                return ((String) o2).compareTo((String) o1);
                //要求加入的元素,按照长度从小到大排序
//                return ((String) o1).length() - ((String) o2).length();
                //要求加入的元素,按照长度从大到小排序
                return ((String) o2).length() - ((String) o1).length();
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");//字符串长度为3
        treeSet.add("sp");
        treeSet.add("a");
//        treeSet.add("abc");//字符串长度为3 加入不进去 因为长度与 treeSet.add("tom");一致,所以加不进去
        System.out.println("treeset=" + treeSet);
    }
}

输出结果如下

treeset=[jack, tom, sp, a]

四、TreeSet的使用

  1. 向TreeSet中添加的数据,要求是相同类的对象
  2. 两种排序方式:自然排序和定制排序
  3. 自然排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不再是equals
@Test
    public void TreeSetTest(){
        TreeSet set = new TreeSet();
        //失败:不能添加不同类的对象
//        set.add("hello");
//        set.add("AA");
//        set.add(12);
//        set.add(12);
//        set.add("cc");
//        set.add(new User("Tom",13));
        //举例1. Integer类型 按照从小到大的顺序排列
//        set.add(23);
//        set.add(77);
//        set.add(-53);
//        set.add(12);
//        set.add(56);
        //举例2. String类型 按照从小到大的顺序排列
//        set.add("23");
//        set.add("abe");
//        set.add("android");
//        set.add("JAVA");
//        set.add("IOS");
        //举例3. 对象 按照从小到大的顺序排列
        set.add(new User("Tom",13));
        set.add(new User("Jack",23));
        set.add(new User("Mary",15));
        set.add(new User("Jerry",20));
        set.add(new User("Jim",33));
        set.add(new User("Jim",33));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

User类:

package www.entity;
import java.util.Objects;
public class User implements Comparable {
    private String name;
    private int age;
    @Override
    public boolean equals(Object o) {
        System.out.println("执行了");
        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);
    }
    public User(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;
    }
    //按照姓名从大到小排列,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if (o instanceof User){
            User user = (User) o;
            int compare = -this.name.compareTo(user.name);
            if (compare!=0){
                return compare;
            }else {
                return Integer.compare(this.age, user.age);
            }
        }else {
            throw new RuntimeException("输入的类型不匹配!");
        }
    }
}

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

@Test
    public void test4() {
        Comparator comparator = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //按照年龄从小到大排序
                if (o1 instanceof User && o2 instanceof User) {
                    User u1 = (User) o1;
                    User u2 = (User) o2;
                    return Integer.compare(u1.getAge(), u2.getAge());
                } else {
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };
        TreeSet set = new TreeSet(comparator);
        set.add(new User("Tom", 13));
        set.add(new User("Jack", 23));
        set.add(new User("Mary", 15));
        set.add(new User("Jerry", 20));
        set.add(new User("Jim", 33));
        set.add(new User("Toke", 33));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

输出结果:年龄从小到大排序,如果有相同年龄的,按自上而下的顺序,输出,后面哪个就不再输出。

目录
相关文章
|
安全 架构师 Java
Java大厂面试高频:Collection 和 Collections 到底咋回答?
Java中的`Collection`和`Collections`是两个容易混淆的概念。`Collection`是集合框架的根接口,定义了集合的基本操作方法,如添加、删除等;而`Collections`是一个工具类,提供了操作集合的静态方法,如排序、查找、同步化等。简单来说,`Collection`关注数据结构,`Collections`则提供功能增强。通过小王的面试经历,我们可以更好地理解这两者的区别及其在实际开发中的应用。希望这篇文章能帮助你掌握这个经典面试题。
351 4
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
206 6
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
193 3
|
存储 算法 Java
Java HashSet:底层工作原理与实现机制
本文介绍了Java中HashSet的工作原理,包括其基于HashMap实现的底层机制。通过示例代码展示了HashSet如何添加元素,并解析了add方法的具体过程,包括计算hash值、处理碰撞及扩容机制。
264 0
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。
【10月更文挑战第14天】从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。本文深入探讨Set的核心理念,并通过示例代码展示了HashSet和TreeSet的特点和应用场景。
144 2
|
4月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
310 1
|
7月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
535 1
|
4月前
|
存储 算法 容器
set_map的实现+set/map加持秒杀高频算法题锻炼算法思维
`set`基于红黑树实现,支持有序存储、自动去重,增删查效率为O(logN)。通过仿函数可自定义排序规则,配合空间配置器灵活管理内存。不支持修改元素值,迭代器失效需注意。`multiset`允许重复元素。常用于去重、排序及查找场景。
|
8月前
|
存储 JavaScript 前端开发
for...of循环在遍历Set和Map时的注意事项有哪些?
for...of循环在遍历Set和Map时的注意事项有哪些?
396 121
|
11月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
300 2