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());
        }
    }

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

目录
相关文章
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
160 57
|
10天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
62 8
|
28天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
60 18
你对Collection中Set、List、Map理解?
|
22天前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
55 20
|
2月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
35 3
【C++】map、set基本用法
|
2月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
26 5
|
3月前
|
存储 JavaScript 前端开发
Set、Map、WeakSet 和 WeakMap 的区别
在 JavaScript 中,Set 和 Map 用于存储唯一值和键值对,支持多种操作方法,如添加、删除和检查元素。WeakSet 和 WeakMap 则存储弱引用的对象,有助于防止内存泄漏,适合特定场景使用。
|
4月前
|
存储 Java API
【数据结构】map&set详解
本文详细介绍了Java集合框架中的Set系列和Map系列集合。Set系列包括HashSet(哈希表实现,无序且元素唯一)、LinkedHashSet(保持插入顺序的HashSet)、TreeSet(红黑树实现,自动排序)。Map系列为双列集合,键值一一对应,键不可重复,值可重复。文章还介绍了HashMap、LinkedHashMap、TreeMap的具体实现与应用场景,并提供了面试题示例,如随机链表复制、宝石与石头、前K个高频单词等问题的解决方案。
52 6
【数据结构】map&set详解
|
3月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
47 1