【学习笔记】集合(三)

集合(三)

 

Set 集合

  • 特点:无序、无下标、元素不能重复

  • 方法:全部继承自Collection中的方法,本身没有定义方法

 

Set 接口的使用

  • 包括数组的创建,添加、删除、遍历、判断操作

package com.collection.set.setDemo;
​
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建数组
        Set<String> set = new HashSet<>();
        //添加元素,没有顺序,并且重复添加,加不进去
        set.add("小米");
        set.add("华为");
        set.add("iphone");
        set.add("小米");
        System.out.println(set);
​
        //删除元素,只能通过对象删除
//        set.remove("iphone");
//        set.clear();
        //遍历集合,不能使用普通的for循环
        System.out.println("-----forEach循环------");
        for (String str:
             set) {
            System.out.println(set);
        }
        System.out.println("-----Iterator迭代器------");
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            String str = it.next();
            System.out.println(str);
        }
​
        //判断
        System.out.println("集合中是否包含华为:"+ set.contains("华为"));
        System.out.println("集合是否为空:" + set.isEmpty());
​
    }
}

image-20220724163310935

 

Set 实现类

 

HashSet

  • HashSet:

    • 基于HashCode 值 计算元素存放位置

    • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者cunru

    • 存储结构是 哈希表(数组 + 链表 + 红黑树)

      • 就像车站售票窗口一样,窗口表示数组,排队的人表示链表

    • HashSet 的方法与 Set 一致

    • 存储过程(也是元素重复的依据)

      • 根据hashCode计算保存的位置(在数组),如果此位置为空,则直接保存,如果不为空执行第二步

      • 执行equals方法,如果equals方法为true,则认为重复,否则,形成链表,也就是在数组那个位置上已经有的那个元素指向新的元素,形成链表

 

我们先创建一个person的集合,并添加元素

package com.collection.set.hashSet;
​
import java.util.HashSet;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建hashSet集合
        HashSet<Person> persons = new HashSet<>();
        //添加元素
        Person p1 = new Person("张三",20);
        Person p2 = new Person("李四",26);
        Person p3 = new Person("王五",23);
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(new Person("张三",20));
        System.out.println(persons);
        //删除元素
​
    }
}

image-20220724172751609

 

我们把已经创建好的对象加入到集合中是可以加入的,但是如果我们加入一个属性相同的对象,它也能加入。

如果 我们想让属性相同的对象不能加入集合中,就要重写hashCode 方法和equals方法

首先通过hashcode 判断它们的属性是否相同,如果相同则返回相同的哈希值,然后在通过equals判断它们是否为同一个对象,它们的属性是否相等

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return age == person.age && Objects.equals(name, person.name);
}
​
@Override
public int hashCode() {
    return Objects.hash(name, age);
}

再去添加属性相同的对象,就添加不进去了

image-20220724173505378

 

删除方法也是相同的道理

//删除元素
persons.remove(new Person("张三",20));
System.out.println(persons);

image-20220724173717333

 

判断是否存在

//判断是否存在
 System.out.println("张三是否存在:"+persons.contains(new Person("张三", 20)));

image-20220724173905841

 

如果只重写equals ,相同属性的对象依旧可以添加到集合中,

原因就是,不重写hashCode 方法,新new 的对象 和与它属性相同的对象 的 哈希值有可能不同,那么,新new的对象的哈希值所对应的数组有可能是空的,那么新new 的对象可以直接放到这个数组里,就不去调用equals 了。

 

补充:

在重写hashCode 方法时,源码如下

@Override
public int hashCode() {
    return Objects.hash(name, age);
}
public static int hash(Object... values) {
    return Arrays.hashCode(values);
}
 public static int hashCode(Object a[]) {
        if (a == null)
            return 0;
​
        int result = 1;
​
        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());
​
        return result;
    }

 

对于为什么要用 31 去乘以 result,有两点原因:

  • 31 是一个质数,可以减少散列冲突,也就是尽量让计算的哈希值不同

  • 提高执行效率,因为 31 * i = (i << 5) - i 可以算的更快 比如 31 * 2 = (2<<5)- 2

 

TreeSet

  • 存储结构:红黑树

  • 基于排列顺序实现元素不重复

  • 实现了SortedSet 接口,对集合元素自动排序

  • 元素对象的类型必须实现Comparable 接口,指定排序规则

  • 通过CompareTo方法确定是否为重复元素

红黑树:二叉查找树,一个节点最多有两个子节点。而且左边的节点都比右边的节点小。比如说我们先添加一个10,那么这个10就是根节点,我们在加一个 7 ,7比10小,所以放在10的左边,在加一个15,这个15应该连在10右边,再加一个 8 ,这个8 应该连在7右边,如果再添加一个7 ,查找后发现有7 了,那么认为7 重复了,就不再添加了

红黑树要求根节点必须是黑的 ,有的节点是黑的,有的是红的,为了保持平衡,不至于导致一边元素特别多,一边特别少

 

TreeSet 的特点与Set相同,只是在添加元素的时候,打印出来时,是按照字母表进行排序的

package com.collection.set.treeSet;
​
import java.util.TreeSet;
​
public class Demo01 {
    public static void main(String[] args) {
        TreeSet<String> tree = new TreeSet<>();
        tree.add("abc");
        tree.add("xyz");
        tree.add("hello");
        tree.add("xxxx");
        System.out.println(tree);
    }
}

image-20220725084336020

遍历时,顺序相同

 

下面我们通过保存Person数据,来说明TreeSet 是如何排序的

package com.collection.set.treeSet;
​
import com.collection.set.hashSet.Person;
​
import java.util.TreeSet;
​
public class Demo02 {
    public static void main(String[] args) {
        TreeSet<Person> persons = new TreeSet<>();
        Person p1 = new Person("张三",20);
        Person p2 = new Person("李四",23);
        Person p3 = new Person("王五",18);
        
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        System.out.println(persons.size());
    }
}

运行时出了异常

image-20220725085059587

类型转换异常:Person类型不能转换成Comparable

原因是:在红黑树中,插入节点时,要进行比较,当我们插入Person类型的对象时,我们没有为它定义比较的规则,所以程序并不知道要比较什么,所以就会报异常

现在我们为其定义比较的规则:要求是元素必须要实现Comparable接口,在Comparable的抽象方法中去实现比较规则:先按姓名比,在按年龄比

package com.collection.set.hashSet;
​
import java.util.Objects;
​
public class Person implements Comparable<Person>{
    private String name;
    private int age;
​
    public Person(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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
​
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
​
    @Override
    public int compareTo(Person o) {
        int n1 = this.name.compareTo(o.getName());
        int n2 = this.age - o.getAge();
        return n1 == 0 ? n2 : n1;
    }
}

 

再去添加Person类型的数据,就可以加进来了

image-20220725090704966

 

删除时,通过new 对象的形式去删除

persons.remove(new Person("王五",16));

可以删除,原因是比较的是姓名和年龄,当我们 new 的这个对象的姓名和年龄与之前的重复时,就可以删除了。

contains 也是一样。

 

补充:Comparator

Comparator可以实现定制比较 ,它是一个比较器,我们在添加元素时可以不用实现Comparable 接口。在创建集合时,指定比较规则。

Comparator是接口,不能直接new ,我们使用匿名内部类的方式创建

package com.collection.set.treeSet;
​
import com.collection.set.hashSet.Person;
​
import java.util.Comparator;
import java.util.TreeSet;
​
public class Demo03 {
    public static void main(String[] args) {
        TreeSet<Person> persons = new TreeSet(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //先比较年龄,在比较姓名
                int n1 = o1.getAge() - o2.getAge();
                int n2 = o1.getName().compareTo(o2.getName());
                return n1 == 0 ? n2 : n1;
            }
        });
​
        Person p1 = new Person("张三",20);
        Person p2 = new Person("李四",23);
        Person p3 = new Person("王五",18);
        Person p4 = new Person("王五",16);
​
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(p4);
        System.out.println(persons);
    }
}

image-20220725092904773

 

案例:

使用TreeSet 集合实现字符串按照长度排序

package com.collection.set.treeSet;
​
import java.util.Comparator;
import java.util.TreeSet;
​
public class Demo04 {
    public static void main(String[] args) {
        TreeSet<String> tree = new TreeSet(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int n1 =  o1.length() - o2.length();
                int n2 = o1.compareTo(o2);
​
                return n1 == 0 ? n2 : n1;
            }
        });
​
        tree.add("helloworld");
        tree.add("zhangsan");
        tree.add("lisi");
        tree.add("wangwu");
        tree.add("xian");
        tree.add("bingjing");
        System.out.println(tree);
    }
}

image-20220725103529086

 
posted @ 2022-07-25 10:37  GrowthRoad  阅读(44)  评论(0编辑  收藏  举报