chapter13 集合框架

Collection的简介及其常用的方法

Collection 接口是 List、Set 和 Queue 接口的父接口,不可以被实列化,即Collection collection = new Collection();但是可以Collection collection = new ArrayList();通过向上转型,从而创建Collection的对象,但是通常的情况下不会直接使用Collection。

Collection的常用方法:
在这里插入图片描述
实例代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

/**
 * collection:允许元素的重复
 * 常用方法:
 * 1、添加:add(E e) 添加e这个元素,E表示的是它的类型,并且返回是否添加成功
 *         addAll(Collection c)添加c中的所有元素,并且返回是否添加成功
 * 2、删除:remove(Object obj) 删除obj这个元素,并且返回是否删除成功
 *         removeAll(Collection c) 删除c中的所有元素,并且返回是否删除成功
 * 3、判断:isEmpty()  判断是否为空
 *         contains(Object obj) 判断是否包含obj这个元素
 *         containsAll(Collection c)判断是否包含c中的所有元素
 * 4、获取:size() 获取collection的元素个数
 *         Iterator()获取迭代器,从而可以遍历collection
 * 5、其他:toArray() 将collection转换成数组
 *         retainAll(Collection c)取交集,从而使调用这个方法的集合变成两
 * 者的交集,并且返回是否取交集成功,即判断调用这个方法的集合是否变成两者
 * 的交集
 */
public class CollectionDemo {
    public static void main(String[] args){
        Collection c = new ArrayList();
        Collection b = new ArrayList();//向上转型
        b.add("abc0");
        b.add("abc1");
        b.add("abc2");
        b.add("abc3");
        System.out.println("b集合的元素个数: "+b.size());
        //添加
        c.add("0ab");
        c.add("0ac");
        c.add("abc1");
        c.add("abc0");
        c.add("abcd");
        System.out.println("c集合的元素个数: "+c.size());
        c.addAll(b);//添加集合框架b中的所有元素
        System.out.println("addAll(b)方法之后,c集合的元素个数: "+c.size());
        //打印c
        System.out.println(c);
        //判断
        System.out.println(c.isEmpty());//调用方法isEmpty()判断是否为空
        System.out.println(c.containsAll(b));//判断c集合是否包含了b中的所有元素,如果是,返回true,否则返回false
        System.out.println(c.contains("abc"));//判断c集合是否包含了abc这个元素

        //迭代c
        display(c);

        //删除
        System.out.println(c.removeAll(b));//删除c集合中b的所有元素,并且返回是否删除成功
        System.out.println("删除b之后打印集合c的所有元素: ");
        display(c);
        System.out.println("删除b之后打印集合c的元素个数: "+c.size());

        //将c变成b、c两者的交集,并且返回是否改变成功,如果二者没有交集,那么就相当于将c的所有元素都删除
        System.out.println("取两个集合的交集是否成功:"+c.retainAll(b));
        //其他
        System.out.println(Arrays.toString(c.toArray()));//将c转换成为数组
    }

    public static void display(Collection c){
        //通过迭代遍历获取集合中的元素
        System.out.println("打印集合c的所有元素: ");
        Iterator it = c.iterator();//调用iterator方法,返回的是Iterator对象
        while(it.hasNext()){//hasNext方法判断是否还有元素
            System.out.print(it.next()+" ");//next方法获取当前的元素
        }
        System.out.println();
    }

}

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

Collection的子接口

List的介绍及常用类

List 是一个有序(所谓的有序,就是输入和输出的顺序是一致的,比如输入-1、3 、2,那么输出的也是-1、3、2,而不是理解的排序输出)、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。

代码示范:

import java.util.*;

/**
 * 学习collection接口的子接口:列表List
 *
 * 方法:
 * 1、添加:add(Object c);//将c元素添加到列表的最后
 *        add(index,element);//将元素添加到index处,如果index处已经有元素了,那么就将原来的元素后移,
 *        然后将新元素插入在角标为index的位置
 *        addAll(Collection c);//将c接口中的所有元素都添加到列表的最后
 *        addAll(index,Collection c);//将c中的所有元素都添加到index的位置,如果index出已经有元素了,
 *        那么原来的元素会后移从而给c腾出位置,然后c的所有元素插入到index处
 *
 *2、删除:remove(Object c);//删除c这个元素,并且返回是否删除成功
 *        remove(int index);//删除下标index的元素,并且返回这个下标对应的元素
 *        removeAll(Collection c);//删除接口c的所有元素,并且返回是否删除成功
 *3、获取:get(index);//获取下标为index的元素
 *        size();//列表中的元素个数
 *        subList(int fromIndex, int endIndex);//获取子列表,注意不包括endIndex下标的元素
 *        indexOf(c);查找c元素对应的下标
 *        lastIndexOf(c);//从最后开始查找c元素,如果找到了,就返回其下标
 *4、替换:set(int index,element)将index下标的元素进行替换成element,并且返回原来index的元素
 *5、判断:isEmpty();//判断列表是否为空
 *
 * 注意不可以在调用Iterator方法进行迭代的时候,在里面使用列表进行相应的操作,比如增删改查等,
 * 否则会抛出异常java.util.ConcurrentModificationException,因为会导致识别错误,
 * 但是可以调用方法listIterator方法进行迭代(是Iterator的子接口),从而可以在里面使用列表进行相应的操作,但是即使
 * 进行操作了,但是输出的和没有操作是一样的,但是列表却真实的发生了改变。但注意的是只有List才可以调用listIterator方法
 */
public class ListDemo {
    public static void main(String[] args){
        List list = new ArrayList();//list是一个接口,而ArrayList不是接口,并且是list的子类
        //添加
        list.add(4);
        list.add(9);
        list.add(9);
        //将新元素添加到角标为2的地方,如果当前角标为2的位置已经有元素了,那么将原来的元素后移,然后新元素插入到角标为2的位置
        list.add(2,10);
        Collection c = new ArrayList();
        c.add(0);
        c.add(7);
        c.add(1);
        //将c中的所有元素都添加到列表的后面
        list.addAll(c);
        //将c中的所有元素添加到角标为2的地方,如果角标为2的位置已经有元素了,那么原来的元素会后移,新元素直接插入到下标为2的位置
        list.addAll(2,c);
        System.out.print("调用iterator方法进行迭代遍历:");
        Iterator i = list.iterator();
        while(i.hasNext()){//调用方法hasNext,从而判断迭代器中是否还存在元素
            Object obj = i.next();//调用方法next,进行获取元素
            //在调用iterator方法进行迭代便利的时候,不可以用集合进行增删等操作,否则抛出ConcurrentModificationException异常,
//            if(obj.equals(new Integer(4))){
//                list.remove(obj);
//            }
            System.out.print(obj+" ");
        }
        System.out.println();
        System.out.println(list);
        System.out.print("调用listIterator方法进行迭代遍历:");
        ListIterator it = list.listIterator();
        while(it.hasNext()){
            Object obj = it.next();
            if(obj.equals(new Integer(4))){
                //注意这里不可以是列表进行调用方法set,否则会发生报错
                it.set(100);//调用方法set,从而将4更改为100
            }
            System.out.print(obj+" ");//迭代完以后输出4 9 0 7 1 10 9 0 7 1 
           // System.out.print(it.next()+" ");//迭代完以后输出9 7 10 0 1
        }
        System.out.println();
        System.out.println(list);//经过更改之后的list
        //获取
        System.out.println(list.lastIndexOf(1));//从最后开始查找1这个元素,如果不存在就返回-1
        System.out.println(list.indexOf(100));//获取100这个元素对应的下标,如果100这个元素不存在,那么返回-1
        System.out.println(list.size());//获取列表的元素个数
        System.out.println(list.subList(2,6));//获取子列表
        System.out.println(list.get(5));//获取下标为5对应的元素
        //获取下标为100对应的元素,如果这个下标超过了列表的元素个数,就会发生报错,提示IndexOutOfBoundsException
        //System.out.println(list.get(100));
        //删除
        System.out.println(list.removeAll(c));//删除c中的所有元素,并且返回是否删除成功
        System.out.println("删除 "+ c+"之后的列表元素为: "+list);
        System.out.println(list.remove(2));//删除角标为2的元素,并且返回对应的元素
        System.out.println(list.remove(new Integer(4)));//这里不可以是remove(4),因为造成的结果是删除下标为4的元素,并将这个元素返回
        //替换
        list.add(1);
        list.add(2);
        list.add(100);
        System.out.println(list);
        System.out.println(list.set(3,7));//将下标为3的元素替换成为7,并且返回原来的元素
        System.out.println(list);
    }
}

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

LinkedList

LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢,因为没有索引,所以只能不断地遍历。这里的随机访问是指检索集合中特定索引位置的元素。

LinkedList特有的方法:
在这里插入图片描述
通过LinkedList实现栈、队列代码:

public class LinkedListTest {
    public static void main(String[] args){
        LinkedList list = new LinkedList();

        //通过LinkedList实现栈的功能
//        list.addFirst("abc1");
//        list.addFirst("abc2");
//        list.addFirst("abc3");
//        list.addFirst("abc4");
//        list.addFirst("abc5");
//        System.out.print("通过LinkedList实现栈:");//通过LinkedList实现栈:abc5, abc4, abc3, abc2, abc1
//        while(!list.isEmpty()){
//            System.out.print(list.removeFirst()+" ");//调用方法removeFirst,从而将第一个元素删除,并将其返回
//        }
        list.addLast("abc1");
        list.addLast("abc2");
        list.addLast("abc3");
        list.addLast("abc4");
        list.addLast("abc5");
        System.out.print("通过LinkedList实现栈:");
        while(!list.isEmpty()){
            System.out.print(list.removeLast()+" ");
        }
        System.out.println();
        //通过LinkedList实现队列的功能:每添加一个元素,就将其放到最后,然后调用方法removeFirst,从而实现先进先出
//        list.addLast("abc0");
//        list.addLast("abc1");
//        list.addLast("abc2");
//        list.addLast("abc3");
//        list.addLast("abc4");
//        System.out.print("通过LinkedList实现队列:");//通过LinkedList实现队列:abc0, abc1, abc2, abc3, abc4
//        while(!list.isEmpty()){
//            System.out.print(list.removeFirst()+" ");//调用方法removeFirst,从而将第一个元素删除,并将其返回
//        }
//        System.out.println();
        list.addFirst("abc0");
        list.addFirst("abc1");
        list.addFirst("abc2");
        list.addFirst("abc3");
        list.addFirst("abc4");
        System.out.print("通过LinkedList实现队列:");//通过LinkedList实现队列:abc0, abc1, abc2, abc3, abc4
        while(!list.isEmpty()){
            System.out.print(list.removeLast()+" ");//调用方法removeFirst,从而将第一个元素删除,并将其返回
        }
        System.out.println();
    }
}

结果:
在这里插入图片描述
通过上面发现:

目的添加删除
实现栈addFirst()/addLast()removeFirst()/ removeLast()
实现队列addLast()/addFirst()removeFirst()/removeLast()

由此可见,实现队列的时候,添加和删除的位置是相反的,实现栈的时候,添加和删除的位置是相同的

因此通过链表实现栈的时候,那么就将每一个元素都要添加到开头,只有这样,才可以直接遍历就实现了栈先进后出的特征,通过链表实现队列的时候,将每一个元素都添加到最后,只有这样,才可以直接遍历链表实现队列先进先出的特征

ArrayList

ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,调用方法get(int index)获得。对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢

ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的所有未实现的方法,只是实现的方式有所不同。
ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。

对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高

Set的介绍及常用子类

Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。Set的方法和Collection一样的。

Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类。

HashSet

HashSet 具有以下特点:

  • 不能保证输出顺序可能与添加顺序相同,即有序。HashSet 是按照 Hash 算法来存储集合中的元素,因此根据元素的哈希值,从而得到元素相应的存储位置,从而使得HashSet输出的顺序和添加顺序有可能不同,但是它的子类LinkedHashSet在保证去重的情况下,同时保证了元素的添加和输出的顺序是一致的
  • HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。

代码演示:

public class HashDemo {
    public static void main(String[] arge){
        HashSet hash = new HashSet();
        LinkedHashSet hash1 = new LinkedHashSet();
        hash.add(2);//输入的是2 8 9 3 2
        hash.add(8);
        hash.add(9);
        hash.add(3);
        hash.add(2);
        hash1.add(2);//输入的是2 8 9 3 2
        hash1.add(8);
        hash1.add(9);
        hash1.add(3);
        hash1.add(2);
        System.out.print("HashSet集合: ");
        display(hash);//输出2 3 8 9
        System.out.print("LinkedHashSet集合: ");
        display(hash1);//输出2 8 9 3
    }

    public static void display(HashSet h){
        Iterator it = h.iterator();
        while(it.hasNext()){
            System.out.print(it.next() +" ");
        }
        System.out.println();
    }
}

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

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置,hashCode()会该对象的内部地址转换成一个整数,因此所有对象的哈希值都是不同的,除非经过了重写。hashCode方法经过重写之后,如果两个元素的哈希值不同,这两个元素必定不相同,但是如果相同,这两个元素不一定相同,比如“ab"和”ba",两者的哈希值相同,但是这两个元素是不同的,此时要调用 equals() 方法比较,如果返回的结果为 true才可以证明这两个元素是相同,否则就不同。两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等

public class HashDemo {
    public static void main(String[] arge){
 //由于String这些类已经重写了Object的equals方法、hashCode方法,所以在hashCode方法返回的不是对象的地址,而是String中定义的返回值,哈希值相同的情况下,equals方法比较两个元素的内容,而不是地址
        String str1 = new String("abc");
        String str2 = new String("abcde");
        String str3 = new String("efg");
        String str4 = new String("abc");//哈希值和str1相同,此时要看内容是否相同词可以断定两个元素是否相同
        String str5 = new String("edabc");//和str2的哈希值相同,因为String类已经重写了hashCode方法,但是内容不同,因此两者是不同的元素,所以会添加到HashSet中
        String str6 = new String("efg");
        HashSet hash = new HashSet();

        hash.add(str1);
        hash.add(str2);
        hash.add(str3);
        hash.add(str4);
        hash.add(str5);
        hash.add(str6);
        
        System.out.print("HashSet集合: ");
        display(hash);
    }

    public static void display(HashSet h){
        Iterator it = h.iterator();
        while(it.hasNext()){
            System.out.print(it.next() +" ");
        }
        System.out.println();
    }
}

结果:
在这里插入图片描述
String类的hashCode()代码:

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

但是有一点需要注意的是,如果添加的元素没有重写Object的equals方法,那么比较的是他们的地址,此时即使他们的内容相同,由于地址不同,从而会再次添加

示例代码:

自定义类(没有重写Object的equals方法,因此比较的是两者的地址,同时没有重写Object的hashCode方法,因此这个类的所有对象的哈希值都不同,因为返回的是对象的地址):

public class Person{
    public String name;
    public int age;

    public Person(String name, int age){
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "{姓名: "+this.name +",年龄: "+this.age +" }";
    }
}

测试类:

import java.util.HashSet;
import java.util.Iterator;

public class HashDemoTest {
    public static void main(String[] args){
        HashSet hash = new HashSet();
        Person p1 = new Person("abc",21);
        Person p2 = new Person("abcd",22);
        Person p3 = new Person("abc",21);
        //打印哈希值
        System.out.println(p1.hashCode());//没有重写hashCode(),因此返回的是对应的地址
        System.out.println(p2.hashCode());
        System.out.println(p3.hashCode());
        hash.add(p1);//hashCode值不一样,那么表明了两个元素必定不同,因此会直接将元素添加到hashSet中
        hash.add(p2);
        hash.add(p3);

        System.out.print("HashSet集合: ");
        display(hash);//
    }

    public static void display(HashSet h){
        Iterator it = h.iterator();
        while(it.hasNext()){
            Person p = (Person)it.next();//next()返回的类型是Object,因此要发生类型强转
            System.out.print(p.toString()+" ");
        }
        System.out.println();
    }
}

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

自定义类重写hashCode方法,但是还没有重写equals方法

public class Person{
    public String name;
    public int age;

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

    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }
}

测试类一样的代码,则结果为:
在这里插入图片描述
在上面的基础上,重写equals方法,从而达到了去重的效果。

自定义类经过重写hashCode方法、equals方法:

public class Person{
    public String name;
    public int age;

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

    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

    @Override
    public boolean equals(Object obj) {//Object obj = new Person(),因为添加的是person这个类的对象
        Person p = (Person)obj;//向下转型
        return this.name.equals(p.name) && this.age == p.age;//判断内容是否相同
    }

    @Override
    public String toString() {
        return "{姓名: "+this.name +",年龄: "+this.age +" }";
    }
}

结果:
在这里插入图片描述
综上所述,只有hashCode方法和equlas方法都相同的情况下,那么这两个元素才是相同的,因此如果往hashSet中添加一些自定义类的时候,需要重写hashCode方法和equals方法,只有这样才不会再添加hashSet中已有的元素,从而使得hashSet中的元素不会重复

TreeSet

TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序

此时需要注意的是,如果添加的元素没有实现Comparable 接口的compartTo方法,即使这个元素已经重写了hashCode方法和equals方法,只要没有实现compareTo方法,那么即使是添加一个元素,进行迭代遍历都会抛出异常,因为不知到如何进行比较从而达到排序的效果

如下面的代码:
自定义类:

public class Person {
    public String name;
    public int age;

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

    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

    @Override
    public boolean equals(Object obj) {
        Person p = (Person)obj;
        return this.name.equals(p.name) && this.age == p.age;
    }

    @Override
    public String toString() {
        return "{姓名: "+this.name +",年龄: "+this.age +" }";
    }

}

测试类:

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

public class TreeSetDemo {
    public static void main(String[] args){
        TreeSet t = new TreeSet();
        
        t.add(new Person("a1",10));
        
        Iterator it = t.iterator();
        while(it.hasNext()){
            Person p = (Person)it.next();
            System.out.print(p.toString()+" ");
        }
        System.out.println();
    }
}

结果:
在这里插入图片描述
因此可以知道即使这个自定义类重写了equals方法、hashCode方法,如果没有实现Comparable 接口的comparaTo方法,都没有办法添加到TreeSet中,进一步表明了TreeSet元素判断是否相同不是看hashCode方法、equals方法,而是看它的compartTo方法,a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值

实现Comparator接口之后的自定义类:

class Person implements Comparable {
    public String name;
    public int age;

    public Person(String name, int age){
        this.age = age;
        this.name = name;
    }
    
    @Override
//重写compareTo方法来自定义比较的顺序,首先根据年龄的大小进行排序,如果年龄相同,那么根据名字的字母顺序进行排序
    public int compareTo(Object o) {
    /**
    *Object o = new Person();向上转型,这个person对象是新插入的,将o对
    *象和TreeSet中的元素进行比较,如果返回值是0,表示新插入的元素已经在
    *TreeSet中存在了,返回正数,表示新插入的元素小,否则大
    */
        Person p = (Person)o;//向下转型
        if(p.age < this.age){//如果新插入的元素年龄小,那么返回正数
            return 1;
        }else{
            if(p.age > this.age)//如果新插入的元素年龄大,那么返回负数
                return -1;
            /**
            *年龄相同,那么比较名字的顺序,如果compareTo方法的返回值大于
            *0,表示新元素的名字在后面,此时返回-1,小于0,表示新元素的名
            *字在前面,等于0,表示名字相同
            */
            if(p.name.compareTo(this.name) > 0)
                return -1;
            else if(p.name.compareTo(this.name) < 0)
                return 1;
            else
                return 0;
        }
    }
}

测试类:

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

public class TreeSetDemo {
    public static void main(String[] args){
        TreeSet t = new TreeSet();
        t.add(new Person("a",10));
        t.add(new Person("b",20));//返回的是-1,表明了当前这个对象(即新添加的元素)大,从而这个对象排在后面
        t.add(new Person("c",15));//返回的是1,表明了当前的对象(即新添加的元素)小,要排在前面
        t.add(new Person("d",20));
        t.add(new Person("a",10));//返回的是0,表明了当前这个对象已经在TreeSet中存在了,因此不在添加,从而保证了元素的唯一性

        Iterator it = t.iterator();
        while(it.hasNext()){
            Person p = (Person)it.next();
            System.out.print(p.toString()+" ");
        }
        System.out.println();
    }
}

结果:
在这里插入图片描述
TreeSet对于元素进行排序的两种方法:
①使添加的元素具备比较的功能,即实现Comparable接口中campareTo方法,从而实现自定义排序,和上面的例子一样。
②使集合自身具备比较的功能。首先构造比较器,即新建一个类,让这个类实现Comparator 接口中campare(Object o1,Object o2)方法,在这两个方法中对两个对象进行比较,从而实现自定义排序,然后在主函数中新建TreeSet对象,同时新建比较器对象,这个比较其对象作为TreeSet对象构造方法的参数即可实现集合自身具备比较的功能

注意两种方式实现的接口是不一样的,因此实现的方法也不一样。前者实现实现Comparable接口中campareTo方法,后者是实现Comparator 接口中campare(Object o1,Object o2)方法

代码实现:
自定义类:

public class Person{
    public String name;
    public int age;

    public Person(String name, int age){
        this.age = age;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "{姓名: "+this.name +",年龄: "+this.age +" }";
    }

}

构造比较器:

import java.util.Comparator;

public class CompareClass implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        Person p1 = (Person)o1;
        Person p2 = (Person)o2;
        if(p1.age < p2.age){
            return -1;
        }else{
            if(p1.age > p2.age)
                return 1;
            if(p1.name.compareTo(p2.name) > 0)
                return 1;
            else if(p1.name.compareTo(p2.name) < 0)
                return -1;
            else
                return 0;
        }
    }
}

测试类:

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

public class TreeSetDemo2 {
    public static void main(String[] args){
        CompareClass c = new CompareClass();//新建比较器对象
        TreeSet t = new TreeSet(c);//新建TreeSet对象,并将比较器对象作为器构造方法的参数
        t.add(new Person("a",10));
        t.add(new Person("b",20));
        t.add(new Person("c",15));
        t.add(new Person("d",20));
        t.add(new Person("a",10));
        
        Iterator it = t.iterator();
        while(it.hasNext()){
            Person p = (Person)it.next();
            System.out.print(p.toString()+" ");
        }
        System.out.println();

    }
}

结果:
在这里插入图片描述
注意在实现相应的方法是,返回值不是固定式-1、1、0等,是按照自己定义的。

Map的介绍及其常用方法

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。

Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false

Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value

常用方法:
在这里插入图片描述

Map的几种遍历方式

Map由于没有iterator方法,因此不可以直接调用iterator方法进行迭代遍历,需要将其转换成Collection或者Set,然后再调用iterator方法进行迭代遍历。

方式一:通过迭代器

import java.util.*;

public class MapDemo {
    public static void main(String[] args){
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();//Map是一个接口,因此不可以直接新建器对象,可以通过向上转型
        map.put(2,22);//如果添加的键再Map中没有存在过,那么添加完毕之后返回null
        map.put(4,24);
        map.put(5,25);
        map.put(2,32);//如果添加的键已经在Map中存在过了,那么新的value就取代原来的value,并且返回原来的value

        //迭代遍历:
        //1、通过调用keySet()方法,从而获得Map的所有键的Set集合,然后调用iterator方法进行迭代遍历,如果要获得对应的值,那么调用get(key)即可
        //2、调用entrySet()方法,从而获得Map的键-值对的Set集合,类型是Map.Entry,然后调用iterator方法进行迭代,然后在这个迭代器中调用方法getKey()获得键,调用方法getValue获得值
        //3、调用values()方法,从而可以得到Map的所有值的Collection集合,然后调用iterator方法进行迭代遍历即可

        Set<Integer> s = map.keySet();
        Iterator<Integer> it = s.iterator();
        while(it.hasNext()){
            int key =  it.next();
            int value = map.get(key);//调用方法get(key),从而获得对应的值
            System.out.print("{ "+key+", "+value+" }"+" ");
        }
        System.out.println();


        Set<Map.Entry<Integer,Integer>> s1 = map.entrySet();
        //调用方法iterator,从而返回一个迭代器对象,可以定义成一个泛型,这样在下面调用next的时候不用类型强转
        Iterator/*<Map.Entry<Integer,Integer>>*/ it1 = s1.iterator();
        while(it1.hasNext()){
            //Map.Entry<Integer,Integer> m = it1.next();将迭代器定义成为Iterator<Map.Entry<Integer,Integer>>时不用类型强转
            Map.Entry<Integer,Integer> m = (Map.Entry<Integer,Integer>)it1.next();
            int key = m.getKey();
            int value = m.getValue();//调用方法get(key),从而获得对应的值,注意返回类型是一个类,因此需要进行强制转换
            System.out.print("{ "+key+", "+value+" }"+" ");
        }
        System.out.println();

        Collection<Integer> c = map.values();//获取所有的value值
        Iterator<Integer> it2 = c.iterator();//调用iterator方法,从而迭代遍历Map中的值
        while(it2.hasNext()){
            System.out.print("{ "+it2.next()+" }");
        }
        System.out.println();
    }
}

结果:
在这里插入图片描述
方式二:循环遍历—通过for-each来实现
注意通过for-each循环便利的时候,定义Map的时候,必须要有参数,即
Map<Integer,Integer> map = new HashMap<Integer,Integer>();,否则会发生报错

public class MapDemo {
    public static void main(String[] args){
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();//Map是一个接口,因此不可以直接新建器对象,可以通过向上转型
        map.put(2,22);//如果添加的键再Map中没有存在过,那么添加完毕之后返回null
        map.put(4,24);
        map.put(5,25);
        map.put(2,32);//如果添加的键已经在Map中存在过了,那么新的value就取代原来的value,并且返回原来的value

        for(Map.Entry<Integer,Integer> entry: map.entrySet()){
             int key = entry.getKey();
             int value = entry.getValue();
             System.out.print("{ "+key+", "+value+" }");
        }
        System.out.println();

        for(int key : map.keySet()){
            int value = map.get(key);
            System.out.print("{ "+key+", "+value+" }");
        }
        System.out.println();

        for(int value : map.values()){
            System.out.print("{ "+value+" }");
        }
        System.out.println();
    }
}

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

Map的常用子类

Hashtable

HashMap内部结构是哈希表,不同步,同时不允许null作为键key或者值value

HashMap

HashMap内部结构是哈希表,不同步,同时允许null作为键key或者值value,这里要和HashTable区分

由上面可以知道,key需要保证唯一性,那么就表明了不可以出现两个key,因此需要去重。那么怎样去重呢?做法和HashSet是一样的

如果添加的键已经重写Object的hashCode()和equals(),那么如果两者都是相同的,那么就表明了新添加的key已经在HashMap中了,那么新添加的value就会替换原来的value,并且将其返回
tamen如果添加的元素是自定义类,那么和HashSet一样,这个自定义类需要重写Object的hashCode()和equals(),只有这样,才可以保证两个元素是否相同,进而达到去重的效果。

提示:如果添加的键key已经存在HashMap中了,那么新添加的value就会取代原来的value,并且返回原来的value值

实例代码:
添加的元素已经重写类Object的hashCode()和equals():

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapDemo {
    public static void main(String[] args){
        HashMap ha = new HashMap();
//如果这个键已经在HashMap中存在过了,那么就会将新的value替换原来的value,并且返回原来的value
//如何判断这个键是否已经在HashMap中存在了呢?和HashSet一样,都要看hashCode方法和equals方法
        ha.put("wangwu",25);//键是一个字符串类型,已经重写了Object的hashCode()和equals()
        ha.put("xiaoshan",13);
        ha.put("xiaoliu",28);
        ha.put("xiaokong",7);
        ha.put("wangwu",20);
        ha.put("xiaoshan",29);

        Set<Map.Entry<String,Integer>> s = ha.entrySet();//调用entrySet()进行迭代遍历
        Iterator<Map.Entry<String,Integer>> it = s.iterator();
        while(it.hasNext()){
            Map.Entry<String,Integer> m = it.next();
            String name = m.getKey();//获取键
            int age = m.getValue();//获取值
            System.out.print("{姓名: "+name+", 年龄: "+age+" }");
        }
        System.out.println();
    }
}

结果:
在这里插入图片描述
添加的元素是自定义类,则需要重写Object的hashCode方法和equals方法,从而保证了key的唯一性,实例代码:
自定义类:

public class Person {
    public String name;
    public int age;

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

    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

    @Override
    public boolean equals(Object obj) {
        Person p = (Person)obj;
        return this.name.equals(p.name) && this.age == p.age;
    }

    @Override
    public String toString() {
        return "{姓名: "+this.name +",年龄: "+this.age +" }";
    }
}

测试类:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args){
        HashMap hash = new HashMap();

        //如果键已经存在了,那么新的value就会取代原来的value,并且返回原来的value值
        //如何判断键已经存在了呢?和HashSet一样,主要看hashCode方法和equals方法
        hash.put(new Person("wangwu",20),"北京");
        hash.put(new Person("zaoliu",27),"上海");
        hash.put(new Person("wanger",22),"深圳");
        hash.put(new Person("wangwu",20),"广州");
        hash.put(new Person("wangwu",18),"北京");
        hash.put(new Person("dongsheng",17),"上海");
        hash.put(new Person("manager",21),"深圳");
        hash.put(new Person("wangwu",28),"广州");

        Set<Map.Entry<Person,String>> s = hash.entrySet();
        Iterator<Map.Entry<Person,String>> it = s.iterator();
        while(it.hasNext()){
            Map.Entry<Person,String> m = it.next();
            Person p = m.getKey();
            String value = m.getValue();
            System.out.println(p.toString()+"----"+value);
        }
    }
}

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

TreeMap

TreeMap 类可以对元素进行排序,它的依据是根据键key来进行排序的。排序的做法和TreeSet一样,同样有两种方式。
做法一:
添加的元素实现了Comparable接口的compareTo方法,从而使得这个元素具有比较的功能
做法二:
使TreeMap自身具有比较的功能,即新建一个比较器,然后将这个比较器作为TreeMap的构造方法的参数,从而实现了这个集合具备比较的功能。
同样的这个比较器需要实现Comparator接口的compare(Objcec o1,Object
o2)方法。

实例代码:

比较器(实现Comparator接口的compare方法,这里首先根据年龄进行排序,如果年龄相同,那么根据姓名的字母顺序进行排序):

import java.util.Comparator;

public class CompareClass implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        Person p1 = (Person)o1;
        Person p2 = (Person)o2;
        if(p1.age < p2.age){
            return -1;
        }else{
            if(p1.age > p2.age)
                return 1;
            if(p1.name.compareTo(p2.name) > 0)
                return 1;
            else if(p1.name.compareTo(p2.name) < 0)
                return -1;
            else
                return 0;
        }
    }
}

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapDemo {
    public static void main(String[] args){
        TreeMap tree = new TreeMap(new CompareClass());//集合自身具备了比较的功能
        //根据键来进行排序,如果键相同,那么新的value会替换原来的value,同时返回原来的value
        tree.put(new Person("wangwu",20),"北京");
        tree.put(new Person("zaoliu",27),"上海");
        tree.put(new Person("wanger",22),"深圳");
        tree.put(new Person("wangwu",20),"广州");
        tree.put(new Person("wangwu",18),"北京");
        tree.put(new Person("dongsheng",17),"上海");
        tree.put(new Person("manager",21),"深圳");
        tree.put(new Person("wangwu",28),"广州");

        Set<Map.Entry<Person,String>> s = tree.entrySet();
        Iterator<Map.Entry<Person,String>> it = s.iterator();
        while(it.hasNext()){
            Map.Entry<Person,String> m = it.next();
            Person p = m.getKey();
            String value = m.getValue();
            System.out.println(p.toString()+"----"+value);
        }
    }
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值