Java SE day 16 集合 Set

集合-Set

1. Set简介

Set接口没有提供Collection接口额外的方法,但实现Set接口的集合类中的元素是不可重复的。

JDKAPI中所提供的Set集合类常用的有:

HashSet:散列存放(重点)

TreeSet:有序存放(重点)

LinkedHashSet: 有次序

2. HashSet集合

2.1 HashSet的实现原理

  1. 是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

  2. 当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。

  3. HashSet的其他操作都是基于HashMap的。

思考:如何把同一个对象在HashSet集合中存入两次?

import java.util.*;
​
public class HashSetDemo {
    public static void main(String[] args) {
        // 创建 HashSet对象
        Set set = new HashSet();
        //加入元素到 HashSet 中
        set.add("F");
        set.add("B");
        set.add("D");
        set.add("E");
        set.add("C");
        set.add("B");//当添加相同的元素时,因为Set集合的元素时唯一的,所以会覆盖之前的B
        System.out.println("set 最初的内容:" + set);
        //删除元素
        set.remove("F");
        System.out.println("set 被改变之后:" + set);
    }
}

输出结果:

通过上面的案例,我们发现Set的特点是无序的,元素是唯一的,我们放元素的顺序是”F—B—D—E--C”,而获取的顺序是”B-C-D-E-F”,同样我们发现Set集合的元素是唯一的,我们往Set集合放了两个”B”,但是在Set集合中只有一个”B”

2.2 hashCode()和equals()

因为hashCode()和equals()方法的返回值共同决定了两个对象是否相等,所以覆写着两个方法时一般要保证两个方法的返回值保证兼容。

重写hashCode()和equals()方法的基本规则:

1、 如果两个对象通过equals()方法比较时返回true,则两个对象的hashCode()方法返回值应该也相等。

2、 对象中用作equals()比较标准的成员变量(属性),也应该参与到hashCode的计算。

3. Iterator(迭代器)接口

3.1 Iterator概述

由于集合中存有很多元素,很多时候需要遍历集合中的所有元素,java专门为集合提供了遍历集合的API:迭代器接口

Iterator是专门的迭代输出接口。所谓的迭代输出就是将元素进行判断,判断是否有内容,如果有内容则把内容取出。

Iterator对象称作迭代器,用以方便的实现对集合内元素的遍历操作。

3.2 获得Iterator对象

调用集合对象的iterator()方法,可以获得一个与该集合对象关联的迭代器对象。

例如:

List<String> list = new ArrayList<>();

Iterator<String>iterator = list.iterator(); //获得Iterator对象

3.3 Iterator的常用方法

Iterator定义如下三个方法:

boolean hasNext(); //判断游标右边是否有元素。如果有返回true,否则false

Object next() ; //返回游标右边的元素并将游标移动到下一个位置

void remove(); //删除游标左面的元素(即next的时候跳过的对象)

注意:迭代方向是单向的,只能从前朝后(Iterator有个专为list集合设计的子接口ListIterator可以实现双向迭代)。

示例代码如下:

package cn.sz.gl.no1;
​
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
​
public class TestIterator {
​
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("abc");
        list.add("bcd");
        Iterator<String> iterator = list.iterator();
​
        while (iterator.hasNext()) {// 判断后面是否还有元素
            String string = iterator.next(); // 获得下一个元素
            System.out.println(string);
​
        }
​
    }
}

注意:在用迭代器遍历集合的时候,如果要删除集合中的元素,只能调用迭代器的remove(),禁止调用集合对象的rmove()方法,否则有可能会出现异常:

java.util.ConcurrentModificationException。//并发访问异常

多学一招:

对于List集合,我们知道List集合是有序的,也就是说,它会给每一个元素添加一个下标,那我们可以使用之前我们学过的for循环去遍历List集合,代码如下:

for(int i = 0; i < list.size(); i++) {          
    System.out.println(list.get(i));        
}

而对于Set集合,我们知道是无序的那我们就不能通过普通for换去获取它们的下标,因为Set元素没有下标,那就是我们不能使用普通for循环来遍历Set集合.

但是我们知道for循环还有一个增强的for循环,对于所有的集合我们可以使用增强for循环来遍历集合:

for(Object ele: list){          
    System.out.println(ele);        
}

3.TreeSet集合

3.1 TreeSet实现原理

TreeSet使用红黑树结构对加入的元素进行排序存放,所以放入TreeSet中元素必须是可“排序”的。

3.2 可排序的对象

TreeSet可是采用两种方法实现排序:自然排序和定制排序。默认情况,TreeSet采用自然排序。

TreeSet调用调用集合元素的CompareTo()方法,根据该方法的返回值来比较元素之间的大小,然后进行“升序”排列,这种排序方式我们称之为自然排列。

注意:如果想采用自然排序,则要存储的对象所属类必须实现Comparable 接口。该接口只有一个方法public int compareTo(Object obj),必须实现该方法。

compareTo方法的实现规则:

返回 0,表示 this == obj。//则不会添加新对象

返回正数,表示 this> obj //添加到原来对象的右边

返回负数,表示 this < obj // 添加到原来对的左边

public class Student implements Comparable<Student> {
    private String name;
    private int 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 String toString() {
        final StringBuilder sb = new StringBuilder("Student").append('[')
                .append("name=")
                .append(name)
                .append(",age=")
                .append(age)
                .append(']');
        return sb.toString();
    }
​
    @Override
    public int compareTo(Student o) {
        //根据年龄升序排序
        int rs =   this.age - o.age;
        if(rs == 0 ){
            if(o == this){
                return 0;
            }else{
                return -1;
            }
        }
        return rs;
    }
}
public class TreeSetDemo {
​
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
​
       treeSet.add(new Student("张三",21));
        treeSet.add(new Student("李四",18));
        treeSet.add(new Student("王五",20));
        treeSet.add(new Student("李琦",19));
        System.out.println(treeSet);
​
    }
}

结果:

3.3 定制排序

使用Comparable接口定义排序顺序有局限性:实现此接口的类只能按compareTo()定义的这一种方式排序。

如果需要更加灵活地排序,我们可以自定义(Comparator)比较器,在创建TreeSet集合对象时把我们自定义的比较器传入,则可以TreeSet会按照我们的比较器中定义的规则进行排序。

自定义比较器类,需要实现Comparator接口。Comparator接口只有一个抽象方法需要实现:public int compare(Object a, Object b);

判断规则:

返回 0,表示a == b

返回正数,表示b > b

返回负数,表示a < b

创建TreeSet集合对象时,把自定义比较器对象传入即可,TreeSet会自动按照比较器中的规则进行排序。

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Student stu1 = (Student)o1;
                Student stu2 = (Student)o2;
                //按照年龄的降序排序
                return  stu2.getAge() - stu1.getAge() ;
            }
        });
       treeSet.add(new Student("张三",21));
        treeSet.add(new Student("李四",18));
        treeSet.add(new Student("王五",20));
        treeSet.add(new Student("李琦",19));
        System.out.println(treeSet);
    }
}
​

结果:

4. LinkedHashSet

根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序

LinkedHashSet集合特点:

底层是一个哈希表(数组+链表/红黑树)+链表,多了一条链表(记录元素的存储顺序),保证元素有序

遍历的时候,LinkedHashSet将会以元素的添加顺序访问集合的元素

LinkedHashSet的方法与它父类HashSet方法是一样的

public static void main(String[] args) {
        LinkedHashSet set = new LinkedHashSet();
        set.add("hello");
        set.add("word");
        set.add("zhangsan");
        set.add("ls");
        set.add("zhangsan");
​
        System.out.println(set);
    }

运行结果:

我们看到set元素输出的顺序与添加元素的顺序是一致的.

如果我们需要迭代的顺序为插入顺序或者访问顺序,并且保证元素的唯一.那么 LinkedHashSet 是需要你首先考虑的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在下张仙人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值