Java语言基础(Set集合)

Set集合概述及特点

Set集合概述及特点:元素唯一 , 一个不包含重复元素的 collection

HashSet存储字符串并遍历

  • HashSet 元素唯一,无序(存取顺序不一致)
  • HashSet 底层数据结构是哈希表:是元素为链表的数组,具有链表和数组的特点 像新华字典(JDK1.7)
  • 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。

代码示例:

import java.util.HashSet;

public class SetDemo {
    public static void main(String[] args) {
        //构建一个set对象
        HashSet<String> set = new HashSet<>();
        set.add("张三");
        set.add("李四");
        set.add("李四");
        set.add("王梅");
        set.add("黎明");
        set.add("张三");
        for (String s : set) {
            System.out.println(s);
        }
     // HashSet 元素唯一,无序(存取顺序不一致) 
    }
}

运行结果:

李四
张三
黎明
王梅

HashSet保证元素唯一性

HashSet 底层数据结构是哈希表.,HashSet 不是线程安全的 集合元素可以是 null
​ 哈希表:是一个元素为链表的数组,综合了数组和链表的优点 (像新华字典一样) (JDK1.7之前)

HashSet的唯一性和无序性:

  1. 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值。
  2. 然后根据 hashCode 值决定该对象所代表的元素在数组中的索引值。
  3. 要放入的元素的值是否和该索引处的链表上的各个元素相等,若不相等就将新的元素接在表尾;若相等就不链接。(保证集合元素的唯一性)
  4. 数据在存入的时候是根据哈希值来确定元素在数组中的索引值。(无序性)

HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。

结论:HashSet 保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证。

代码示例:

import java.util.HashSet;

public class SetDemo2 {
    public static void main(String[] args) {
        HashSet<Integer> set = new HashSet<>();
        set.add(100);

        //长按ctrl,选择HashSet点击打开
        //   public HashSet() {
        //    map = new HashMap<>();
        //}

        //长按ctrl,选择add点击打开
        //public boolean add (E e){
        //    return map.put(e, PRESENT) == null;
        //}
        //长按ctrl,选择put 点击打开
        //public V put (K key, V value){
        //    return putVal(hash(key), key, value, false, true);
        //}

        //长按ctrl,选择putVal 点击打开 
//        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
//        boolean evict) {
//            Node<K,V>[] tab; Node<K,V> p; int n, i;
//            if ((tab = table) == null || (n = tab.length) == 0)
//                n = (tab = resize()).length;
//            if ((p = tab[i = (n - 1) & hash]) == null)
//                tab[i] = newNode(hash, key, value, null);
//            else {
//                Node<K,V> e; K k;
//                if (p.hash == hash &&
//                        ((k = p.key) == key || (key != null && key.equals(k))))
//                    e = p;
//                else if (p instanceof TreeNode)
//                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//                else {
//                    for (int binCount = 0; ; ++binCount) {
//                        if ((e = p.next) == null) {
//                            p.next = newNode(hash, key, value, null);
//                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
//                                treeifyBin(tab, hash);
//                            break;
//                        }
//                        if (e.hash == hash &&
//                                ((k = e.key) == key || (key != null && key.equals(k))))
//                            break;
//                        p = e;
//                    }
//                }
//                if (e != null) { // existing mapping for key
//                    V oldValue = e.value;
//                    if (!onlyIfAbsent || oldValue == null)
//                        e.value = value;
//                    afterNodeAccess(e);
//                    return oldValue;
//                }
//            }
//            ++modCount;
//            if (++size > threshold)
//                resize();
//            afterNodeInsertion(evict);
//            return null;
//        }

        //static final int hash (Object key){
        //    int h;
        //    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        //}


        set.add(200);
        set.add(100);
        set.add(300);
        set.add(90);
        set.add(109);
        for (Integer integer : set) {
            System.out.println(integer);
        }
    }
}

HashSet存储自定义对象保证元素唯一性

代码示例:

import java.util.HashSet;

public class MyTest {
    public static void main(String[] args) {
        //HashSet集合能够保证元素的唯一性,是靠元素重写hashCode()方法和equals方法来保证的,
        // 如果元素不重写则无法保证
        //HashSet 底层用的是HashMap来存的
        Student s1 = new Student("王五", 25);
        Student s2 = new Student("王五", 25);
        Student s3 = new Student("王五", 25);

        Student s4 = new Student("王五", 252);

        Student s5 = new Student("王五2", 235);
        Student s6 = new Student("王五3", 25);
        Student s7 = new Student("王五4", 25);
        
        HashSet<Student> hashSet = new HashSet<>();
        hashSet.add(s1);
        hashSet.add(s2);
        hashSet.add(s3);
        hashSet.add(s4);
        hashSet.add(s5);
        hashSet.add(s6);
        hashSet.add(s7);
        for (Student student : hashSet) {
            System.out.println(student.getName() + "==" + student.getAge());
        }
    }
}

class Student  {
    private String name;
    private int 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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        int hash = Objects.hash(name, age);
        return hash;
    }
}

运行结果:

王五4==25
王五==25
王五3==25
王五==252
王五2==235

HashSet存储自定义对象保证元素唯一性图解

Java语言基础(Set集合)

LinkedHashSet的概述

数据结构 有两个 链表和哈希表
链表保证有序 哈希表保证元素唯一

代码示例:

import java.util.LinkedHashSet;

public class MyTest {
   public static void main(String[] args) {
       // LinkedHashSet 底层数据结构是链表和哈希表,元素有序且唯一 ,链表保证了元素有序,哈希表保证了元素
       LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
       linkedHashSet.add("A");
       linkedHashSet.add("B");
       linkedHashSet.add("D");
       linkedHashSet.add("E");
       linkedHashSet.add("C");
       linkedHashSet.add("E");
       linkedHashSet.add("C");
       linkedHashSet.add("E");
       linkedHashSet.add("C");

       for (String s : linkedHashSet) {
           System.out.println(s);
       }
   }
}
A
B
D
E
C

TreeSet存储Integer类型的元素并遍历

TreeSet集合的特点: 底层数据结构是二叉树,元素唯一,他最大的特点是能够对元素进行排序
排序:
1. 自然排序(空参构造)
2. 使用比较器排序(有参构造)
到底使用的是哪一种的排序取决于,构造方法.
1. 用空参构造,那么就使用的是自然排序
2. 有参构造,可以使用比较器来排序
注意:使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素
必须实现Comparable接口 否则无法进行自然排序
保证元素的唯一性是靠compareTo方法的返回值来确定如果返回0 表示两个元素相等

代码示例:

 import java.util.TreeSet;

public class MyTest {
    public static void main(String[] args) {
        // TreeSet 底层数据结构是二叉树,元素唯一,他最大的特点是能够对元素进行排序
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(20);
        treeSet.add(18);
        treeSet.add(23);
        treeSet.add(22);
        treeSet.add(17);
        treeSet.add(24);
        treeSet.add(19);
        treeSet.add(18);
        treeSet.add(24);
        for (Integer integer : treeSet) {
            System.out.println(integer);
        }
    }
}	

运行结果:

17
18
19
20
22
23
24

代码示例2:

import java.util.TreeSet;
import java.util.Objects;

public class MyTest {
    public static void main(String[] args) {
        //按照学生的年龄大小来排序
        Student s1 = new Student("王五", 21);
        Student s11 = new Student("王五", 21);
        Student s123 = new Student("王五3333333333", 21);
        Student s2 = new Student("王五", 22);
        Student s3 = new Student("王五111", 25);
        Student s4 = new Student("王五3333", 252);
        Student s5 = new Student("王五22222222222", 235);
        Student s6 = new Student("王五3", 25);
        Student s7 = new Student("王五2222222224", 2665);
        Student s8 = new Student("王五5", 288);
        Student s9 = new Student("王五22226", 285);
        Student s10 = new Student("王五7", 255);
        TreeSet<Student> treeSet = new TreeSet<>();
        treeSet.add(s1);
        treeSet.add(s11);
        treeSet.add(s2);
        treeSet.add(s3);
        treeSet.add(s4);
        treeSet.add(s5);
        treeSet.add(s6);
        treeSet.add(s7);
        treeSet.add(s8);
        treeSet.add(s9);
        treeSet.add(s10);
        treeSet.add(s123);
        //如果我们使用的是自然排序:那么对元素是有要求的,要求元素必须实现一个Comparable接口重写里面的comPareTo方法,根据此方法的返回值的正负0 来决定元素在二叉树的位置
        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}

public class Student implements Comparable<Student> {
    private String name;
    private int 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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }


    @Override
    public int compareTo(Student s) {
        //比较逻辑是按照年龄大小来排序
        int num = this.age - s.age;
        //当年龄相同不能说明他是同一个对象,还得比较姓名
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        //if(num==0){
        //    num=this.name.compareTo(s.name);
        //}
        return num2;
    }
}

运行结果:

Student{name='王五', age=21}
Student{name='王五3333333333', age=21}
Student{name='王五', age=22}
Student{name='王五111', age=25}
Student{name='王五3', age=25}
Student{name='王五22222222222', age=235}
Student{name='王五3333', age=252}
Student{name='王五7', age=255}
Student{name='王五22226', age=285}
Student{name='王五5', age=288}
Student{name='王五2222222224', age=2665}

代码示例2:

import java.util.Comparator;
import java.util.TreeSet;

public class MyTest3 {
    public static void main(String[] args) {
        //TreeSet:可以使用比较器排序,你采用有参构造,给他传入一个比较器

        //构造方法摘要
        //TreeSet()
        //构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。


        //TreeSet(Comparator < ? super E > comparator)
        //构造一个新的空 TreeSet,它根据指定比较器进行排序。

        //接口 Comparator<T >
        //int compare (T o1, T o2)
        //比较用来排序的两个参数。


        Comparator comparator = new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //按照姓名长度来拍
                int num = s1.getName().length() - s2.getName().length();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                return num3;
            }
        };

        TreeSet<Student> treeSet = new TreeSet<>(comparator);
        treeSet.add(new Student("王五", 21));
        treeSet.add(new Student("王五1111", 212));
        treeSet.add(new Student("王五21111", 241));
        treeSet.add(new Student("王五3111", 2771));
        treeSet.add(new Student("王五4111", 251));
        treeSet.add(new Student("王五5111", 2661));
        treeSet.add(new Student("王五611111", 2661));
        treeSet.add(new Student("王五7", 21733));
        treeSet.add(new Student("王五7", 21733));
        treeSet.add(new Student("王五7", 21337));
        treeSet.add(new Student("王五7", 217));
        treeSet.add(new Student("王五7", 217));

        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}

public class MyComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        //按照年龄来排
        int num = s1.getAge() - s2.getAge();
        int num2=num==0?s1.getName().compareTo(s2.getName()):num;
        return num2;
    }
}

public class Student {
    private String name;
    private int 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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}

运行结果:

Student{name='王五', age=21}
Student{name='王五7', age=217}
Student{name='王五7', age=21337}
Student{name='王五7', age=21733}
Student{name='王五1111', age=212}
Student{name='王五3111', age=2771}
Student{name='王五4111', age=251}
Student{name='王五5111', age=2661}
Student{name='王五21111', age=241}
Student{name='王五611111', age=2661}