Collection接口下的Set、List

Collection接口

介绍

Collection接口是List接口和Set接口的父接口,通常情况不会直接使用,Collection接口中定义了一些通用的方法,通过他们可以实现对集合的添加、删除、等基本操作。List接口和Set接口实现了Collection接口,所有这些方法对List和Set集合是通用的。

Collection接口的常用方法

add(E obj):将指定的对象添加到集合中

addAll(Collection<? extends E> col):将指定集合中的所有对象添加 到该集合中。如果对该集合使用了泛型,则要求该集合中的所有对象都符合泛型类型。否则在编译时会抛出异常。

remove(Object obj):将指定的对象从该集合中删除。返回值为boolean,如果存在指定的对象则返回true,否则 返回false 。

clear() :从此集合中删除所有元素

removeAll(Collection<?> c) :移除所有对象

retainAll(Collection<?> c) :该方法仅保留指定集合中的所有对象,其他全部移除。

contains(Object o) :如果此集合包含指定的元素,则返回 true 。

containsAll(Collection<?> c) :查看该集合中是否存在指定集合中的所有对象。

isEmpty() :查看该集合是否为空。返回值为Boolean ,不存在任何对象就返回true ,否则false

size():获得该集合存在对象个数 ,返回类型为Int 。

clear():清空该集合

iterator():序列化该集合中的所有对象。返回值为Iterator型,通过返回的Iterator的实例可以遍历集合中的对象

toArray():获得一个包含所有对象的Object数组

 Object objects[]= hashSet.toArray();
        for (int i = 0; i <objects.length ; i++) {
            System.out.println(objects[i].toString());
        }

equals(Object obj):查看指定的对象与该对象是否 为同一个对象。

Set集合

Set集合包括Set接口以及它的所有实现类。Set接口继承自Collection,所以Set接口拥有Collection接口所提供的所有方法。Set接口本身没有声明其他方法。实现Set接口的几个实用类:HashSet、TreeSet、EnumSet类。

HashSet类

HashSet的底层是用HashMap、TreeSet用TreeMap实现的,这里先不讲底层后面的章节会讲

Set集合中的对象是无序的并且是不可重复的,但是这种无序并不是完全无序,只是不像List集合中那样按照插入顺序保存对象。

不同的对象有不同的哈希码算法例如: 
1、Object类的hashCode返回对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。 
2、String类的hashCode根据String类包含的字符串的内容,根据一种特殊算法返回哈希码。只要字符串所在的堆空间相同,返回的哈希码也相同。 
3、Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如: 
Integer i1=new Integer(100); i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。 

用HashSet实现的Set集合按照哈希码排序,并根据对象的哈希码来确定对象的存储位置,所以在添加对象时要重写hashCode()方法。

在Object类中的 hashCode()(既原生的hashCode),是不可能返回两个相同的哈希码(哈希码唯一标志了对象,通俗的说就是属性相同的两个对象由于使用了new生成对象,那么它俩存储在内存中内存地址一定不一样),但是!但是!但是!比如a对象的姓名、学号为(王五,1),b对象的姓名、学号也为(王五,1),这两个对象调用原生的hashCode的时候返回的哈希码一定不相同,所以此时a和b对象都会存储在HashSet集合中(如下面的案例1)。卧槽了。这个时候就会违反Set集合的不可重复规则。


这个时候原生的hashCode就会让程序的运行不符合现实世界的逻辑(如:在现实世界在班上不可能出现两个同学的姓名学号都是(王五,1) ,除非你们的老师脑子有坑。。。。。这个就另说了,八成他会被炒鱿鱼)。


此时此刻我就就需要重写hashCode()方法,目的是让他们返回相同的哈希码,哈希码相同则不插入对象。


用HashSet实现的Set集合中的对象必须是唯一的(不可重复,第一种说法:在我们的业务系统中判断对象时有时候需要的不是一种严格意义上的相等(地址),而是一种业务上(内容)的对象相等。),所以这个时候原生的equals已经不能满足要求,因此在添加对象时要重写equals()方法。

到了这里,同学们可能又有新的疑问了,既然重写了hashCode了,为啥还要重写equals()。

回答:唉,人生处处充满意外。第二种说法:有些属性不同的对象(当然是不同的对象),会返回相同的哈希码(既重码)

最后为了解决重码的问题,当hashCode返回的哈希码相同的时候,再用equals方法比较两个对象的对应属性是否相同,这样,确保了万无一失。

案例1:不重写hashCode()和equals()时会发生什么
Person.java

public class Person implements{
    private String name;
    private long sno;

    Person(String name,long sno){
        this.name = name;
        this.sno = sno;
    }
    public String getName() {
        return name;
    }
    public long getSno() {
        return sno;
    }

}

HashSetTest.java

public class HashSetTest {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Set<Person> hashSet = new HashSet<Person>();
        hashSet.add(new Person("张三", 2));
        hashSet.add(new Person("李四", 3));
        hashSet.add(new Person("寒寒韩", 0));
        hashSet.add(new Person("王五", 1));
        hashSet.add(new Person("王五", 1));
//Set集合不可重复(但是在现实世界中“名字:王五,学号:1”当姓名和学号都相同的时候我们认为这是同一个人或者说是同一个对象)
        Iterator<Person> it = hashSet.iterator();
        while (it.hasNext()) {
            Person person = it.next();
            System.out.println(person.getName()+"---"+person.getSno());
        }

    }
}

运行结果

在这里插入图片描述
Set元素不可重复,但是如果在Person类中不重写hashCode()和equals(),那么就会违背Set元素不可重复的规则或者说违背了现实世界中的逻辑(其实第一个王五-1和第二个王五-1在计算机上面是不同的对象,既内存地址不同,但是不符合现实世界中的逻辑第一个和第二个都是同一个人。如果你的班级上面有两个人同名,但是学号肯定不同。当然不可能存在两个同学 学号相同的情况)

因此用HashSet实现Set集合的时候需要重写hashCode和equals。

重写hashCode和equals之后的案例:

Person.java

public class Person{
    private String name;
    private long sno;

    Person(String name,long sno){
        this.name = name;
        this.sno = sno;
    }
    public String getName() {
        return name;
    }
    public long getSno() {
        return sno;
    }

    @Override
    public int hashCode(){//重写hashCode()

        final int PRIME = 31;
        int results=1;
        results = PRIME * results + (int)(sno^(sno>>>32));
        results = PRIME *results + ((name==null)? 0: name.hashCode());
        return results;
    }
    @Override
    public boolean equals(Object obj){
        if(this==obj)//地址相等:为同一个对象不插入
            return true;
        if(obj==null)//obj等于空则插入
            return false;
        if(getClass()!=obj.getClass())//getClass返回调用这个方法的类,正在运行的类
            return false;//正在运行的类不等于传进参数的类,则插入
        Person other=(Person) obj;
        if(sno!=other.sno)
            return false;//如果当前对象与传入的对象的学号不等则插入对象
        if(name==null){
            if(other.name!=null)
                return false;
        }else if (!name.equals(other.name))
            return false;
        return true;
    }


}

运行结果:
在这里插入图片描述

TreeSet类

TreeSet类不仅仅实现了Set接口,还实现了java.util.SortedSet接口,从而保证在遍历集合的时按照递增的顺序获取对象。

TreeSet实现了SortedSet接口之后所增加的方法如下:

  • comparator():获得对该对象使用的比较器,返回值为Comparator类型,如果未使用任何 比较器则返回null;
  • first() : 返回集合 中排序位于第一的对象;
  • last() : 返回集合 中排序位于最后的对象;
  • headSet(E toElement) : 截取集合中位于对象toElement 之前的所有对象(不包括toElement),重新生成一个Set集合并返回
  • subSet(E from , E toElement) : 截取位于对象from(包含)和toElement(不包含)之间的对象,重新生成一个Set集合并返回
  • tailSet(E toElement):截取位于toElement对象(包含)之后的所有对象,重新生成一个Set集合并返回

递增遍历对象有两种类型
                第一种:按照自然顺序递增排列,此时存入由TreeSet类实现的Set集合的对象必须实现Comparable接口

public class Person implements Comparable{}

Set<Person> treeSet = new TreeSet<Person>();

                第二种:按照指定比较器(Comparator)递增排列,既可以通过对由TreeSet类实现的Set集合中的对象进行排序。

自然排序的案例:(重写compareTo()方法)

Person.java

package com.set;

public class Person implements Comparable{
    private String name;
    private long sno;

    Person(String name,long sno){
        this.name = name;
        this.sno = sno;
    }
    public String getName() {
        return name;
    }
    public long getSno() {
        return sno;
    }

  
 
    @Override
    public int compareTo(Object o) {//默认按照学号升序排序。 1表示往后插入,0表示相等不插入,-1表示往前插入
        Person person = (Person)o;
        int result = sno>person.sno? 1:(sno==person.sno? 0:-1);
        return result;
    }

}

TreeSetTest.java

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args){

        Person person1 = new Person("小明",2);
        Person person2 = new Person("小胡",1);
        Person person3 = new Person("王五",3);
        Person person4 = new Person("张三",0);
        Person person5 = new Person("王五",4);
        Person person6 = new Person("王",4);


        Set<Person> treeSet = new TreeSet<Person>();
        treeSet.add(person1);
        treeSet.add(person2);
        treeSet.add(person3);
        treeSet.add(person4);
        treeSet.add(person5);
        treeSet.add(person5);

        Iterator<Person> iterator = treeSet.iterator();
        while (iterator.hasNext()){
            Person person = iterator.next();
            System.out.println(person.getName()+"--"+person.getSno());
        }
    }
}

在这里插入图片描述
在这里插入图片描述

插入的第一个数据为中心(person1),在add(person2)之前先调用compareTo()方法,中心点person1与person2比较(比较的是sno),如果return -1,往前插,如果return 1往person的后面插,return 0则不插入。

List集合

List接口属于列表类型,列表的主要特征是以线性的方式存储对象,因此List接口是一种有序的集合。除了继承Collection接口中声明的方法之外,List接口还增加了一些按位置(索引)存取元素,查找等新的操作。

List适用于自身的常用方法:

  • void add(int index ,Object obj):向集合的指定索引位置添加对象,其他对象索引位置相对后移一位(索引位置从0开始)
  • abstract boolean addAll(int index ,Collection c):向集合中指定索引位置添加指定集合中的对象
  • Object remove(int index ):清除集合中指定索引位置的对象
  • Object set(int index ,Object obj):将集合中指定索引位置的对象修改为指定的对象
  • Object get(int index):获取指定索引位置的对象
  • int indexOf(Object obj):获取指定对象的索引位置,存在多个 相同的对象时候,返回第一个索引位置,当不存在时返回 -1
  • int lastIndexOf(Object o):获取指定对象的索引位置,当存在多个的时候返回最后一个索引位置,不存在的时候返回 -1
  • ListIterator listIterator():获取一个包含所有对象的ListIterator类型实例,
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值