文章目录

  • 1.Set接口概述
  • 2.HashSet集合
  • 2.1.HashSet集合存储数据的结构(哈希表)
  • 2.2.HashSet存储JavaAPI中的类型元素
  • 2.3.HashSet存储自定义类型元素
  • 3.TreeSet集合
  • 3.1.自然排序Comparable(内部比较器)
  • 3.2.比较器排序Comparator(外部比较器)
  • 3.3.自然排序和比较器排序比较
  • 3.4.TreeSet练习
  • 4.LinkedHashSet集合
  • 4.1.特点
  • 5.总结


1.Set接口概述

  • Set集合可以去除重复元素
  • Set集合的存入顺序和取出顺序不一致
  • Set集合没有索引,不能使用普通for循环遍历

查阅Set集合的API介绍,通过元素的equals方法,来判断是否为重复元素,它是个不包含重复元素的集合。Set集合取出元素的方式可以采用:迭代器、增强for。
Set集合有多个子类,这里我们介绍其中的HashSetTreeSetLinkedHashSet这两个集合。

该集合中没有特有的方法,直接继承自Collection

---| Itreable      接口 实现该接口可以使用增强for循环
	---| Collection		描述所有集合共性的接口
		---| List接口	    可以有重复元素的集合
                         ---| ArrayList   
                         ---|  LinkedList
		---| Set接口	    不可以有重复元素的集合
  • 案例:set集合添加元素并使用迭代器迭代元素。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Demo4 {
	public static void main(String[] args) {
		//Set 集合存和取的顺序不一致。
		Set hs = new HashSet();
		hs.add("世界军事");
		hs.add("兵器知识");
		hs.add("舰船知识");
		hs.add("汉和防务");
		System.out.println(hs);
		// [舰船知识, 世界军事, 兵器知识, 汉和防务]
		Iterator it = hs.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

Set接口下面,没有定义get( )方法,那么就意味着Set没有下标。

2.HashSet集合

2.1.HashSet集合存储数据的结构(哈希表)

.首先明确一点,hashset实现了set接口,因此是无序的,这个无序是指,元素输入和输出的顺序。也就是不管添加元素的顺序是什么,遍历输出该集合时,都是按照存储的顺序来的,一般输出顺序和输入顺序不同,即使相同也是巧合。

第一个:

HashSet list = new HashSet();
 list.add(1);
 list.add(2);
 list.add(17);
 list.add(33);

结果:

1
17
33
2

第二个:

HashSet<Integer> list = new HashSet<>();
 list.add(1);
 list.add(17);
 list.add(2);
 list.add(33);

结果:

1
17
33
2

结论:输入输出,是无序的,素的存储是按照一定机制来的,输出就是按照这个顺序,因此这两个输出结果顺序一样。

只有搞懂hashset的存储机制原理才能够了解输出顺序

hashset的存储过程

JavaSE(十九)-Set接口_JavaSe

1.首先获取要存储元素的hash值,
2.然后用hash值 %(模)表的长度(哈希表初始数组长度为16,加载因子0.75,每次扩容1倍,也就是说,当元素个数大于16*0.75=12时,数组长度扩大一倍,变成32)得到的余数为数组的下标。然后将元素加到此中。
3.如果不同的元素经过2步骤处理后得到的余数相同,则之前在这个位置的元素,挂到新元素下面(形成一个链表),如果,下挂元素超过8个,则链表转换为红黑树。

代码分析

HashSet<Integer> list = new HashSet<>();
list.add(1);
list.add(2);
list.add(17);
list.add(33);

结果:

1
17
33
2
1.添加1时,对元素 模 表长度 也就是1%16 余1,在数组下标为1的位置插入元素1
2.添加2时,对元素 模 表长度 也就是2%16 余2,在数组下标为2的位置插入元素2
3.添加17时,对元素 模 表长度 也就是17%16 余1,在数组下标为1的位置插入元素17,元素17下挂到元素1下(尾插法)
4.添加33时,对元素 模 表长度 也就是33%16 余1,在数组下标为1的位置插入元素33,元素33下挂到元素17下

此时遍历集合,按照数组下标升序输出,同一下标有多个值,从上往下依次输出,
所以输出为 1,17,33,2

2.2.HashSet存储JavaAPI中的类型元素

HashSet中存储JavaAPI中提供的类型元素时,不需要重写元素的hashCodeequals方法,因为这两个方法,在JavaAPI的每个类中已经重写完毕,如String类、Integer类等。

  • 创建HashSet集合,存储String对象。
publicclass HashSetDemo {
	publicstaticvoid main(String[] args) {
		//创建HashSet对象
		HashSet<String> hs = new HashSet<String>();
		//给集合中添加自定义对象
		hs.add("zhangsan");
		hs.add("lisi");
		hs.add("wangwu");
		hs.add("zhangsan");
		//取出集合中的每个元素
		Iterator<String> it = hs.iterator();
		while(it.hasNext()){
			String s = it.next();
			System.out.println(s);
		}
	}
}

输出结果如下,说明集合中不能存储重复元素:

wangwu
lisi
zhangsan

2.3.HashSet存储自定义类型元素

HashSet中存放自定义类型元素时,需要重写对象中的hashCodeequals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一

  • 创建自定义对象Student
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        super();
        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() {
        return Objects.hash(name, age);
    }
}
  • 创建HashSet集合,存储Student对象。
public class HashSetDemo {
    public static void main(String[] args) {
        //创建HashSet对象
        HashSet hs = new HashSet();
        //给集合中添加自定义对象
        hs.add(new Student("zhangsan", 21));
        hs.add(new Student("lisi", 22));
        hs.add(new Student("wangwu", 23));
        hs.add(new Student("zhangsan", 21));
        //取出集合中的每个元素
        Iterator it = hs.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();
            System.out.println(s);
        }
    }
}

输出结果如下,说明集合中不能存储重复元素:

Student [name=lisi, age=22]
Student [name=zhangsan, age=21]
Student [name=wangwu, age=23]

问题:现在有一批数据,要求不能重复存储元素,而且要排序。ArrayList 、 LinkedList不能去除重复数据。HashSet可以去除重复,但是是无序。

3.TreeSet集合

不可以存储重复元素
没有索引,不能使用普通for循环 
可以将元素按照规则进行排序(自然排序和比较器排序)
  • 案例:使用TreeSet集合存储字符串元素,并遍历
import java.util.TreeSet;

public class Demo5 {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet();
		ts.add("ccc");
		ts.add("aaa");
		ts.add("ddd");
		ts.add("bbb");

		System.out.println(ts); // [aaa, bbb, ccc, ddd]

	}
}

既然TreeSet可以自然排序,那么TreeSet必定是有排序规则的。

使用元素的自然顺序对元素进行排序,或者根据创建 set时提供的 Comparator进行排序(比较器排序)

JavaSE(十九)-Set接口_JavaSe_02

public static void main(String[] args) {
     TreeSet 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);
     System.out.println("集合:" + treeSet);
 }

3.1.自然排序Comparable(内部比较器)

  • 使用空参构造创建TreeSet集合
  • 自定义的Student类实现Comparable接口
  • 重写接口中的compareTo方法
public class Student implements Comparable {
    private String name;
    private int age;


    @Override
    public String toString() {
        return "Student{" +
                "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;
    }

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

    public Student() {
    }

    @Override
    public int compareTo(Object o) {
        Student s = (Student) o;
        //主要判断条件
        int result = this.age - s.age;
        //次要判断条件
        result = this.age == s.age ? this.name.compareTo(s.name) : result;
        return result;
    }
}

测试类:

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet students = new TreeSet();
 
        Student stu1 = new Student("zhangliang", 59);
        Student stu2 = new Student("likui", 43);
        Student stu3 = new Student("abc", 38);
        Student stu4 = new Student("acb", 38);
        Student stu5 = new Student("zhouyu", 26);
        students.add(stu1);
        students.add(stu2);
        students.add(stu3);
        students.add(stu4);
        students.add(stu5);

        for (Object student : students) {
            System.out.println(student);
        }
    }
}

3.2.比较器排序Comparator(外部比较器)

TreeSet集合的构造方法接收Comparator的实现类对象(匿名内部类),重写compare方法

案例需求:

使用TreeSet集合存储老师对象并遍历,创建TreeSet集合使用带参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

public class Teacher {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Teacher{" +
                "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;
    }

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

    public Teacher() {
    }
}

测试类

public class TreeSetDemo1 {
    public static void main(String[] args) {
        //使用匿名内部类创建Comparator实现类,重写compare方法
        TreeSet teachers = new TreeSet(new MyComparator());

        Teacher teacher1 = new Teacher("abc", 26);
        Teacher teacher2 = new Teacher("acb", 26);
        Teacher teacher3 = new Teacher("wangling", 19);
        Teacher teacher4 = new Teacher("deshrng", 56);
        Teacher teacher5 = new Teacher("hangjinh", 36);
        teachers.add(teacher1);
        teachers.add(teacher2);
        teachers.add(teacher3);
        teachers.add(teacher4);
        teachers.add(teacher5);

        for (Object teacher : teachers) {
            System.out.println(teacher);
        }
    }
}

class MyComparator implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        Teacher t1 = (Teacher) o1;
        Teacher t2 = (Teacher) o2;
        int result = t1.getAge() - t2.getAge();
        result = result == 0 ? t1.getName().compareTo(t2.getName()) : result;
        return result;
    }
}

3.3.自然排序和比较器排序比较

  • 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
  • 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序

两种方式中关于返回值的规则

如果返回值为负数,表示当前存入的元素是较小值,存左边
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
如果返回值为正数,表示当前存入的元素是较大值,存右边

注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;

3.4.TreeSet练习

将字符串中的数值进行排序。

例如

String str="8 10 15 5 2 7";    // 2,5,7,8,10,15
  • 使用 TreeSet完成。
1,将字符串切割。
2,可以将这些对象存入TreeSet集合。

因为TreeSet自身具备排序功能。

public class Demo5 {
	public static void main(String[] args) {
		String str = "8 10 15 5 2 7";
		String[] strs = str.split(" ");
		TreeSet ts = new TreeSet();
		for (int x = 0; x < strs.length; x++) {
			int y = Integer.parseInt(strs[x]);
			ts.add(y);
		}
		System.out.println(ts);
	}
}

4.LinkedHashSet集合

public class LinkedHashSet<E> extends HashSet<E>
        implements Set<E>, Cloneable, java.io.Serializable

4.1.特点

  • 此类在java.util包中可用。
  • 这是Set接口的实现类。
  • LinkedHashSet类是HashSet类的子级。
  • 在LinkedHashSet中,保留插入顺序,这意味着元素的插入顺序必须与元素的检索顺序相同。
  • 在Java 1.4的早期版本中引入了LinkedHashSet类。
  • 如果元素的插入顺序很重要,则应使用LinkedHashSet。

示例

假设我们有一个包含少量元素的LinkedHashSet。在这里,我们以[10,20,30,50,null]的顺序添加元素,如果我们要检索元素,则检索元素的顺序必须相同(即,其插入和检索顺序必须相同)。元素。)因此输出将相同,并且顺序将类似于[10,20,30,50,null]。

class LinkedHashSetClass {
    public static void main(String[] args) {
        //创建LinkedHashSet的实例
        LinkedHashSet lhs = new LinkedHashSet();

        //通过使用add()方法将元素添加到LinkedHashSet-
        lhs.add(10);
        lhs.add(20);
        lhs.add(30);
        lhs.add(50);
        lhs.add(null);

        //显示LinkedHashSet元素
        System.out.println("LinkedHashSet is :" + lhs);
    }
}

5.总结

JavaSE(十九)-Set接口_Java集合_03

  • 看到array,就要想到小标。
  • 看到link,就要想到firstlast
  • 看到hash,就要想到hashCode,equals.
  • 看到tree,就要想到两个接口。ComparableComparator