Java基础知识 21(Set集合,HashSet集合以及它的三种遍历方式(迭代器,增强for循环,forEach),LinkedHashSet集合,TreeSet集合(自然排序法,比较器排序法))

Java基础知识 21

Set集合

Set集合:一个不包含重复元素的Collection集合,元素不重复,List集合是允许元素重复的。
Set接口的三个字类:HashSet(),LinkedHashSet(),TreeSet()

HashSet集合

Java基础知识 21(Set集合,HashSet集合以及它的三种遍历方式(迭代器,增强for循环,forEach),LinkedHashSet集合,TreeSet集合(自然排序法,比较器排序法))_第1张图片
HashSet()集合底层使用的是hashMap()来存储元素,底层数据结构是哈希表,元素无序,且不允许元素重复(存的顺序和去的顺序不一致),可以存储null元素,线程不安全,效率高。哈希表:是一个元素为链表的数组,综合了数组和链表的优点(JDK1.7之前),(JDK1.8之后优化了数组+链表+二叉树)
HashSet(),构造一个新的容器空set,其底层是HashMap实例,默认初始容量是16,加载因子是0.75。

import java.util.HashSet;

public class Mytest {
    public static void main(String[] args) {
        /*
         * Set接口:一个不包含重复元素的Collection,元素不重复。
         * list:允许元素重复
         * Set接口的三个字类:HashSet,LinkedHashSet,TreeSet
         * HashSet:底层数据结构是哈希表,元素无序(存的顺序和取的顺序不一致),
         * 且不允许元素重复,可以存储null元素,线程不安全的,但是效率高
         */
        //HashSet(),构造一个新的空set,其底层HashMap实例的默认初始容量是16,加载因子是0.75
        HashSet hashSet = new HashSet<>();
        hashSet.add("王俊凯");
        hashSet.add("王源");
        hashSet.add("易烊千玺");
        hashSet.add("王俊凯");
        hashSet.add("王源");
        hashSet.add("易烊千玺");
        hashSet.add("王俊凯");
        hashSet.add("王源");
        hashSet.add("易烊千玺");
        System.out.println(hashSet);
        System.out.println("---------------------");
        for (String s : hashSet) {
            System.out.println(s);
        }
    }
}

当我们往HashSet集合中存储对象时,会调用hashCode()方法,算出一个值,这个值就是确定这个对象放到表中的位置。那假如有两个对象,算出的位置是一样的,就会调用equals()方法,去比较两个对象的地址值是否一样,如果不一样那就存储进去,形成链表结构。

import java.util.HashSet;

public class Mytest {
    public static void main(String[] args) {
        /*
         *HashSet底层数据结构是哈希表,HashSet是线程不安全的,集合元素可以是null
         *哈希表:是一个元素为链表的数组,综合了数组和集合的所有优点(像新华字典一样)
         * (JDK1.7之前)JDK1.8之后优化了(数组+链表+二叉树)
         */
        //存储元素为student类型,无序且不允许元素重复
        HashSet hashSet = new HashSet<>();
        hashSet.add(new Student("夏雪",25));
        hashSet.add(new Student("刘星",24));
        hashSet.add(new Student("夏雨",23));
        hashSet.add(new Student("夏雪",25));
        hashSet.add(new Student("刘星",24));
        hashSet.add(new Student("夏雨",23));
        for (Student student : hashSet) {
            System.out.println(student);
        }
        /*当我们往hashset集合中存储对象时,会调用对象的hashcode方法,得到一个值,
        这个值就是确定这个对象放到表中的位置*/
        /*那如果我们有两个对象算出来的位置值是一样的,就会调用equals()方法,去比较两个对象的地址值是否相同,
        如果地址值不相同,那就存储进去形成链表结构。*/

    }
}
---------------------------------
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 boolean equals(Object o) {
        System.out.println("equals调用了 this:"+this+"==="+"之前的元素"+o);
        //由于调用equals方法,碰撞次数过多,因此我们去比较 他们的成员变量值
        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);
        //return super.equals(o);
    }

    @Override
    public int hashCode() {
        //return Objects.hash(name, age);
        //如果写死hashCode值,肯定会造成碰撞次数过多
        return 0;
    }

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

为了减少碰撞,我们应该合理的去重写hashCode方法,来减少碰撞(减少调用equals方法的次数),尽量是在哈希表中横向排列,减少链表的形成。

public class Mytest2 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet<>();
        hashSet.add(1000);
        hashSet.add(2000);
        hashSet.add(3000);
        hashSet.add(1000);
        hashSet.add(2000);
        hashSet.add(3000);
        hashSet.add(1000);
        hashSet.add(2000);
        hashSet.add(3000);
        System.out.println(hashSet);
        System.out.println("-----------------");
        for (Integer integer : hashSet) {
            System.out.println(integer);
        }
        //String类和Integer类,已经重写了hashCode()方法和equals()方法
    }
}

HashSet集合能够保证元素唯一性的原因重写了hashCode()方法和equals()方法来保证的,如果元素不重写hashCode()方法和equals()方法,则无法保证元素的唯一性。
我们合理的重写hashCode()方法,是为了元素能够在哈希表中尽可能的横向排列,减少碰撞(减少equals方法的调用)

import org.westos.demo2.Student;

import java.util.HashSet;

public class Mytest {
    public static void main(String[] args) {
        /*
         *HashSet底层数据结构是哈希表,HashSet是线程不安全的,集合元素可以是null
         *哈希表:是一个元素为链表的数组,综合了数组和集合的所有优点(像新华字典一样)
         * (JDK1.7之前)JDK1.8之后优化了(数组+链表+二叉树)
         */
        //存储元素为student类型,无序且不允许元素重复
        HashSet hashSet = new HashSet<>();
        hashSet.add(new Student("夏雪",25));
        hashSet.add(new Student("刘星",24));
        hashSet.add(new Student("夏雨",23));
        hashSet.add(new Student("夏雪",25));
        hashSet.add(new Student("刘星",24));
        hashSet.add(new Student("夏雨",23));
        for (Student student : hashSet) {
            System.out.println(student.getName() + "===" + student.getAge());
        }
        /*当我们往hashset集合中存储对象时,会调用对象的hashcode方法,得到一个值,
        这个值就是确定这个对象放到表中的位置*/
        /*那如果我们有两个对象算出来的位置值是一样的,就会调用equals()方法,去比较两个对象的地址值是否相同,
        如果地址值不相同,那就存储进去形成链表结构。*/

        /*hashSet:集合能保证元素的唯一性,是靠元素重写hashCode()方法和equals()方法来保证的。
        如果元素不重写hashCode()和equals()方法,则无法保证元素的唯一性。
        我们合理的重写hashCode()方法是为了元素能够在哈希表中尽量横线分布,减少碰撞(减少equals方法的调用)*/
    }
}
----------------------------------
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 boolean equals(Object o) {
        //System.out.println("equals调用了 this:"+this+"==="+"之前的元素"+o);
        //由于调用equals方法,碰撞次数过多,因此我们去比较 他们的成员变量值
        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);
        //return super.equals(o);
    }

    @Override
    public int hashCode() {
        //return Objects.hash(name, age);
        //如果写死hashCode值,肯定会造成碰撞次数过多
        return this.name.hashCode()+this.age*5;
    }*/

    @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() {
        return Objects.hash(name, age);
    }

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

HashSet集合的三种遍历方式:

import java.util.HashSet;
import java.util.Iterator;
import java.util.function.Consumer;

public class Mytest {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet<>();
        hashSet.add(100);
        hashSet.add(200);
        hashSet.add(300);
        //hashSet集合遍历的三种方式
        //1.使用迭代器进行遍历
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            Integer next = iterator.next();
            System.out.println(next);
        }
        System.out.println("-------------------");
        //2.使用增强for循环来进行遍历
        for (Integer integer : hashSet) {
            System.out.println(integer);
        }
        System.out.println("-------------------");
        //3.使用forEach()方法来进行遍历
        hashSet.forEach(new Consumer() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
    }
}

对元素进行去重:HashSet(Collection c),构造一个包含指定 collection 中的元素的新 set。你把List集合传进来,构建一个HashSet集合,就帮你去重了。

import java.util.ArrayList;
import java.util.HashSet;

public class Mytest2 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        list.add(100);
        list.add(100);
        list.add(100);
        list.add(200);
        list.add(200);
        list.add(200);
        list.add(300);
        list.add(300);
        list.add(300);
        System.out.println(list);
        /*对元素进行去重
        HashSet(Collection c)
        构造一个包含指定 collection 中的元素的新 set。
        你把List集合传进来,构建一个HashSet集合,就帮你去重了*/
        HashSet hashSet = new HashSet<>(list);
        System.out.println(hashSet);
    }
}

LinkedHashSet集合

LinkedHashSet集合的底层数据结构是链表+哈希表,链表能够保证元素的有序,哈希表能够保证元素的唯一性。(但是还是要重写hashCode方法和equals方法),LinkedHashSet元素有序且唯一,线程不安全的,但效率高。

import java.util.LinkedHashSet;

public class Mytest {
    public static void main(String[] args) {
        /*
         *LinkedHashSet 底层数据结构是链表+哈希表,
         * 链表能够保证元素有序,哈希表能够保证元素唯一。(但是还是要重写hashCode()和equals()方法)
         * LinkedHashSet 元素有序且唯一,线程不安全,但效率高
         * hashSet:无序,唯一
         */
        LinkedHashSet linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("唐僧");
        linkedHashSet.add("孙悟空");
        linkedHashSet.add("猪八戒");
        linkedHashSet.add("沙悟净");
        linkedHashSet.add("唐僧");
        linkedHashSet.add("孙悟空");
        linkedHashSet.add("猪八戒");
        linkedHashSet.add("沙悟净");
        //遍历元素
        for (String s : linkedHashSet) {
            System.out.println(s);
        }
    }
}

TreeSet集合

Java基础知识 21(Set集合,HashSet集合以及它的三种遍历方式(迭代器,增强for循环,forEach),LinkedHashSet集合,TreeSet集合(自然排序法,比较器排序法))_第2张图片
TreeSet:底层数据结构使用的是二叉树,元素唯一,且能对元素进行排序。
TreeSet集合对元素进行排序有两种方法:自然排序法和比较器排序法。
(1)自然排序法:当你使用空参构造时,就是自然排序法。如果使用自然排序法,自然排序对元素有要求,要求元素必须实现一个Comparable接口,重写这个接口中的compareTo方法,这个比较的方法是根据返回值是 0 正 负 来决定元素在二叉树中放的位置,以及是否往进放。

import java.util.TreeSet;

public class Mytest{
    public static void main(String[] args) {
        //使用自然排序:来排序学生对象,根据学生的年龄大小 来排序
        TreeSet treeSet = new TreeSet<>();
        treeSet.add(new Student("诸葛亮",50));
        treeSet.add(new Student("周瑜",48));
        treeSet.add(new Student("刘备",52));
        treeSet.add(new Student("关羽",58));
        treeSet.add(new Student("张飞",45));
        treeSet.add(new Student("曹操",55));
        treeSet.add(new Student("诸葛亮",50));
        treeSet.add(new Student("关羽",58));
        //如果这里出现了年龄一样,但是名字不一样的情况。这时候就需要我们在适当修改一下compareTo方法
        treeSet.add(new Student("孙权",52));
        //遍历元素
        for (Student student : treeSet) {
            System.out.println(student.getName()+"======"+student.getAge());
        }
    }
}
-------------------------------------------
public class Student implements Comparable{
    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 int compareTo(Student o) {
        //按照年龄的大小来比较
        int num=this.age-o.age;
        //年龄相同,但是名字不一样的情况,这种就需要比较姓名是否相同
        //使用三目运算符进行判别上面这种情况
        int num2=num==0 ? this.name.compareTo(o.name):num;
        ////返回值的 正 负  0 来决定元素在二叉树中的放置的左右位置,返回0 就不往里面存储
        return num2;
        //return 0;//返回是0,你写死了,只能返回一个根元素。
    }

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

(2)比较器排序法: TreeSet(Comparator comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。Comparator接口,强行对某个对象Collections进行了整体排序比较函数。采用比较器排序,根据比较器中的compare()方法的返回值的正 负 0 ,来决定元素在二叉树中放的位置。

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

public class Mytest {
    public static void main(String[] args) {
        //使用比较器排序,按照姓名的长度来排序
        TreeSet treeSet = new TreeSet<>(new Comparator() {
            @Override
            public int compare(Student s1, Student s2) {
                //先比较姓名的长度
                int num1 = s1.getName().length() - s2.getName().length();
                //姓名长度一样时,再比较年龄大小
                int num2=num1==0?s1.getAge()-s2.getAge():num1;
                //年龄相同时,再比较姓名的内容
                int num3=num2==0?s1.getName().compareTo(s2.getName()):num2;

                return num3;
            }
        });

        treeSet.add(new Student("吴用智多星",50));
        treeSet.add(new Student("黑旋风李逵", 57));
        treeSet.add(new Student("宋江及时雨", 57));
        treeSet.add(new Student("武松行者", 57));
        treeSet.add(new Student("花和尚鲁智深", 23));
        treeSet.add(new Student("立地太岁阮小二", 97));
        treeSet.add(new Student("扈三娘", 47));
        treeSet.add(new Student("白胜", 17));
        treeSet.add(new Student("时迁", 17));
        treeSet.add(new Student("西门大官人", 87));

        for (Student student : treeSet) {
            System.out.println(student.getName()+"==="+student.getAge());
        }
    }
}
----------------------------------------
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 +
                '}';
    }

}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class Mytest2 {
    public static void main(String[] args) {
        //Comparator 这个比较器,不光TreeSet能用,其他的有些类也能用
        //可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort)
        ArrayList list = new ArrayList<>();
        list.add(1000);
        list.add(800);
        list.add(950);
        list.add(860);
        list.add(503);
        list.add(640);
        list.add(999);
        list.sort(new Comparator() {
            @Override
            public int compare(Integer a, Integer b) {
                return a-b;
            }
        });
        System.out.println(list);
        System.out.println("------------------");
        int[] arr={20,30,90,41,33};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
        System.out.println("------------------");
        Integer[] arr2={20,30,1,-5,90,41,33};
        //参数2:可以传入比较器
        Arrays.sort(arr2, new Comparator() {
            @Override
            public int compare(Integer a, Integer b) {
                return b-a;
            }
        });
        System.out.println(Arrays.toString(arr2));
    }
}

你可能感兴趣的:(Java基础知识 21(Set集合,HashSet集合以及它的三种遍历方式(迭代器,增强for循环,forEach),LinkedHashSet集合,TreeSet集合(自然排序法,比较器排序法)))