Java进阶之Collections&Set接口&Map接口

一、Collections

Collections是一个操作集合的工具类(注意与Colletion的区别)。

1.1 常用方法

  • static void shuffle(List<?> list):打乱集合中内容的顺序
  • static void sort(List list):对集合中的元素进行排序(自然排序)
  • static void sort(List list, Comparator c):对集合中的内容进行排序,参数c表示比较器(比较器排序)
  • static boolean addAll(Collection c, T... elements):批量添加元素
    参数c:表示向哪个集合添加元素。
    参数elements:表示要添加那些元素。 该参数是可变参数,可以向该参数位置传递任意个数据

1.1.1 自然排序

自然排序指的是事物本身就具备比较的功能,在程序中指集合里面的泛型必须要实现Comparable接口。

如果类实现Comparable接口,那么表示该类的对象就具备了比较的功能,就可以使用Collections.sort方法对对集合中的元素进行排序。

当使用sort方法进行排序(自然排序)时,内部会自动调用compareTo方法比较两个元素的大小:

  • 如果该方法的返回值是正数,表示调用者对象大于参数对象
  • 如果该方法的返回值是0,表示两个对象相等
  • 如果该方法的返回值是负数,表示调用者对象小于参数对象

实现Comparable接口需要做的是在compareTo方法中编写排序的规则

公式:

  • 升序就是(调用者)减(参数对象)
  • 需要什么属性进行排序,就让对应的属性值相减
public static void main(String[] args) {
        //创建集合,保存整数
        List<Integer> intList = new ArrayList<>();
        //向集合中添加元素
        intList.add(200);
        intList.add(100);
        intList.add(300);
        //对集合中的内容进行排序
        Collections.sort(intList);
        //输出结果
        System.out.println("intList:" + intList);//[100, 200, 300]
        System.out.println("===================================");

        //创建集合,保存字符串
        List<String> strList = new ArrayList<>();
        //添加字符串
        strList.add("bbb");
        strList.add("aaa");
        strList.add("ccc");
        //对集合中的内容进行排序
        Collections.sort(strList);
        //输出结果
        System.out.println("strList:" + strList);//[aaa, bbb, ccc]

}

public class Person implements Comparable<Person>{
    private String name;
    private int age;
    @Override
    public int compareTo(Person o) {
        //根据年龄进行升序排序。
        return this.getAge() - o.getAge();
    }
}

1.1.2 比较器排序

如果某个事物本身不具备比较的功能,那么我们就不能使用自然排序的方式对集合直接进行排序了。

如果事物不具备不叫的功能,那么我们可以找一个裁判(比较器)帮这些对象去比较,这样集合也可以进行排序。

Comparator是一个接口,该接口表示比较器,如果要用该比较器则需要使用实现类,这个实现类需要我们自己定义。

步骤:

  • 1.创建集合并添加元素
  • 2.定义一个类,实现Comparator接口
  • 3.重写compare方法,并在该方法中定义比较的规则
  • 4.调用Collections的sort方法,传递集合和比较器进行排序

当调用Collections的sort方法进行比较器排序时,那么系统内部会自动调用compare方法比较两个对象的大小。

  • 如果该方法返回值是正数,说明第一个参数大于第二个参数。
  • 如果该方法的返回值是0,说明两个对象相等。
  • 如果该方法的返回值是负数,说明第一个参数小于第二个参数。
排序公式:
升序 第一个参数减第二个参数
/*
    Comparator表示比较器,Rule类实现了Comparator接口,Rule类也就表示比较器了
 */
public class Rule implements Comparator<Student>{
   
    @Override
    public int compare(Student o1, Student o2) {
        //根据年龄升序排序
        return o1.getAge() - o2.getAge();
    }
}
public class Student {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
	get.set.构造方法
}
	public static void main(String[] args) {
        //创建集合并添加元素
        List<Student> list = new ArrayList<>();
        //添加元素
        list.add(new Student("jack", 20));
        list.add(new Student("rose", 18));
        list.add(new Student("tom", 22));
        //进行排序
        Collections.sort(list, new Rule());
        //输出结果
        System.out.println(list);
    }

1.1.3 小结

  • 如果事物本身就具备比较的功能,那么我们可以直接使用sort方法进行排序
  • 如果事物本身不具备比较的功能,那么我们可以找一个裁判帮这些对象进行排序

1.1.4 shuffle方法

	public static void main(String[] args) {
        //创建集合
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        list.add("php");
        //输出集合
        System.out.println("list:" + list);//[hello,world,java,php]
        //对集合中的内容打乱顺序
        Collections.shuffle(list);
        //输出集合
        System.out.println("list:" + list);//[world,hello,php,java]
    }

1.1.5 addAll方法

学习addAll方法之前,我们首先要了解什么是可变参数。

可变参数是JDK5的个新特性。

如果一个方法的参数是可变参数,那么可以在该参数位置传递任意个数据。

可变参数格式:
        修饰符 返回值类型 方法名(参数类型... 参数名) {
            方法体;
            return 返回值;
        }
  • 1.在调用可变参数的方法时,可以向可变参数位置传递任意个数据
  • 2.可变参数的本质就是数组,所以可以将可变参数当成数组去使用
  • 3.以为可变参数的本质是数组,所以调用可变参数的方法时,也可以向可变参数位置传递数组

注意:

  • 1.一个方法中最多只能有一个可变参数
  • 2.方法中可变参数必须在最后一个位置
public static int getSum(int... nums) {
        //定义变量sum保存累加和
        int sum = 0;
        //遍历nums,将每一个元素累加到sum
        for (int num : nums) {
            sum += num;
        }
        return sum;
    }

public static void main(String[] args) {
        int sum = getSum(1,2,3,4,5);
        System.out.println(sum);

        int[] arr = {1,2,3};
        int sum = getSum(arr);
        System.out.println(sum);
    }

了解了可变参数后,我们再来看addAll方法

  • static boolean addAll(Collection c, T... elements):批量向集合中添加元素。
    参数c:表示向哪个集合中添加元素
    参数elements:是一个可变参数,可以向该参数位置传递任意个数据。 该参数表示要添加的元素
public static void main(String[] args) {
        //创建集合
        List<String> list = new ArrayList<>();
        //使用Collections中的addAll批量添加元素
        Collections.addAll(list, "hello", "world", "java", "php");

        System.out.println(list);//[hello,world,java,php]

    }

二、Set接口

2.1 Set接口概述

Set是Collection下面的一个子接口,不能直接使用,需要使用实现类。

Set接口有以下特点:

  • 无索引(不能根据索引获取元素的)
  • 不可重复(不能保存重复元素)
  • (大部分Set集合满足的特点)无序(按照什么顺序存,不一定按照什么顺序取)
	public static void main(String[] args) {
        //创建Set集合
        Set<String> set = new HashSet<>();
        //向集合中添加元素
        set.add("张三丰");
        set.add("张无忌");
        set.add("灭绝师太");
        set.add("金花婆婆");
        //输出集合
        //无序
        System.out.println(set);//[灭绝师太, 张三丰, 张无忌, 金花婆婆]
        //无索引
        //System.out.println(set.get(1));
        //不可重复
        set.add("灭绝师太");
        set.add("灭绝师太");
        set.add("灭绝师太");
        set.add("灭绝师太");
        System.out.println(set);//[灭绝师太, 张三丰, 张无忌, 金花婆婆]
    }

2.2 Set接口遍历

因为Set集合是没有索引的,所以不能使用普通for遍历,可以使用迭代器或增强for遍历,推荐使用增强for遍历。

	public static void main(String[] args) {
        //创建Set集合
        Set<String> set = new HashSet<>();
        //添加元素
        set.add("张三丰");
        set.add("张无忌");
        set.add("灭绝师太");
        set.add("金花婆婆");
        //使用增强for遍历集合(和之前增强for遍历一模一样)
        for (String str : set) {
            System.out.println(str);
        }
    }

2.3 哈希值

哈希值是一个int数字,我们可以将哈希值看成对象的一个标识(特征码),如同每个人对应的身份证号码。

在Object中有一个方法叫做hashCode,可以获取对象的哈希值:

  • int hashCode():获取对象的哈希值

这个方法是根据对象的地址值计算的哈希值,但是根据地址值计算一般来说意义不大,我们更多的是希望哈希值是根据属性计算的,如果两个对象的属性完全相同,那么哈希值也应该相同。

如果想要自己定义哈希值的计算规则,需要重写hashCode方法。

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

    //alt + insert 直接生成hashCode方法
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;//31是根据数学概率计算的值,让哈希值重复的概率适中
        return result;
    }
}

	public static void main(String[] args) {
        //创建Person对象
        Person p = new Person("张三丰", 100);
        //获取该对象的哈希值然后输出
        System.out.println(p.hashCode());


        Person p2 = new Person("张三丰", 100);
        System.out.println(p2.hashCode());
        System.out.println(p1.hashCode()==p2.hashCode());//true
    }

哈希值是对象的一个标识,但并不是唯一的标识,对象的哈希值允许重复。

2.4 哈希表

在这里插入图片描述

2.5 HashSet保证唯一性

HashSet判断唯一性的过程

  • 1.先比较对象的哈希值。
    如果哈希值不同,肯定是不同的对象。
    如果哈希值相同,不一定是同一个对象。
  • 2.如果哈希值相同,还会调用equals进行比较。
    如果equals的结果是true,表示对象相同。
    如果equals的结果是false,表示对象不同

结论:
如果使用HashSet存储自定义对象并保证唯一性(对象的属性相同就看成是同一个对象),需要同时重写hashCode和equals,缺一不可。

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    get..set..构造方法
}

	public static void main(String[] args) {
        //创建HashSet集合
        Set<Student> set = new HashSet<>();
        //添加元素
        set.add(new Student("柳岩", 36));
        set.add(new Student("李小璐", 34));
        set.add(new Student("马蓉", 32));

        set.add(new Student("柳岩", 36));

        //遍历集合,输出集合中的每一个学生对象
        for (Student stu : set) {
            System.out.println(stu);
        }

    }

三、Map接口

3.1 Map概述

我们之前学的Collection是单列集合,即一次只保存一个数据。

Map是双列集合,里面的每一个元素都是键值对,一个键对应一个值。Map<K,V>有两个泛型,K表示键的类型,V表示值的类型。
在这里插入图片描述

3.2 Map常用方法

  • (常用)V put(K key, V value):向Map集合中添加键值对元素。key表示键,value表示值。如果在添加时key重复,会使用新的值覆盖掉原来的值
  • (常用)V get(Object key):根据键获取对应的值
  • boolean containsKey(Object key):判断map集合中是否包含指定的键
  • V remove(Object key):根据键删除整个的键值对,返回值是被删除掉的值

Map是一个接口,如果要用需要使用实现类,最常用的实现类是HashMap();

	public static void main(String[] args) {
        //创建双列集合,key使用整数(编号),value使用字符串(人名)
        Map<Integer, String> map = new HashMap<>();
        //(常用)V put(K key, V value):向Map集合中添加键值对元素
        map.put(100, "刘德华");
        map.put(200, "张学友");
        map.put(300, "郭富城");
        //添加元素
        map.put(200, "黎明");
        //输出
        System.out.println(map);
        // (常用)V get(Object key):根据键获取对应的值。
        System.out.println("获取100键对应的值:" + map.get(100));//刘德华
        System.out.println("获取10000键对应的值:" + map.get(10000));//null 如果获取的键不存在,结果是null。
        //boolean containsKey(Object key):判断map集合中是否包含指定的键。
        System.out.println("判断Map集合中是否有100键:" + map.containsKey(100));
        System.out.println("判断Map集合中是否有10000键:" + map.containsKey(10000));
        //V remove(Object key):根据键删除整个的键值对,返回值是被删除掉的值。
        //删除100这个键对应的整个元素
        String value = map.remove(100);
        System.out.println(map);//{200=黎明, 300=郭富城}
        System.out.println("value:" + value);//刘德华
    }

3.3 Map的遍历

3.3.1 keySet遍历

Map集合不能通过迭代器或者增强for直接进行遍历。

如果要遍历Map集合,我们可以获取到Map集合中所有的键,将所有的键放入到Set集合中, 然后遍历Set集合,拿到集合中的每一个键,然后根据键去Map集合中获取对应的值。

Map中有一个方法叫做keySet,可以获取到所有的键

  • Set<K> keySet():获取Map集合中所有的键,并放入到Set集合中返回

keySet遍历步骤:

  • 1.调用Map集合的keySet方法获取到所有的键,然后放入到Set集合中。
  • 2.遍历Set集合,拿到里面的每一个键。
  • 3.调用Map集合的get方法,根据键获取对应的值

在这里插入图片描述

	public static void main(String[] args) {
        //创建集合
        Map<String, String> map = new HashMap<>();
        //添加元素
        map.put("it001", "张三");
        map.put("it002", "李四");
        map.put("it003", "王叔叔");
        //开始遍历
        //1. 调用Map集合的keySet方法获取到所有的键,然后放入到Set集合中。
        Set<String> keys = map.keySet();
        //2. 遍历Set集合,拿到里面的每一个键。
        for (String key : keys) {
            //3. 调用Map集合的get方法,根据键获取对应的值。
            String value = map.get(key);
            System.out.println(key + "-----" + value);
        }
    }

3.3.2 entrySet遍历

Map集合还有一种遍历方式是通过Entry对象的方式进行遍历。

Map集合中每一个元素其实都是一个键值对,每一个键值对都是一个Entry对象。

如果要通过Entry对象的方式遍历Map集合,我们需要先获取到Map集合中所有的Entry对象,将这些Entry对象放入到一个Set集合,然后 遍历Set集合,拿到Set集合中每一个Entry对象,根据该Entry对象获取键以及对应的值。

在Map集合中有一个方法叫做entrySet,可以获取到所有的Entry对象:

  • Set<Map.Entry<K,V>> entrySet():获取到所有的Entry对象并放入到Set集合中返回

Entry中获取键和值的方法:

  • K getKey():获取到Entry对象中的键
  • V getValue():获取Entry对象中的值

entrySet遍历步骤:

  • 1.调用Map集合的entrySet方法获取到所有的Entry对象并放入到Set集合中返回
  • 2.遍历Set集合,拿到里面的每一个Entry对象
  • 3.调用Entry对象的getKey和getValue获取到键和值

在这里插入图片描述

	public static void main(String[] args) {
        //创建集合
        Map<String, String> map = new HashMap<>();
        //添加元素
        map.put("it001", "张三");
        map.put("it002", "李四");
        map.put("it003", "王叔叔");
        //开始遍历
        //1. 调用Map集合的entrySet方法获取到所有的Entry对象并放入到Set集合中返回
        Set<Map.Entry<String, String>> entries = map.entrySet();
        //2. 遍历Set集合,拿到里面的每一个Entry对象
        for (Map.Entry<String, String> entry :entries) {
            //3. 调用Entry对象的getKey和getValue获取到键和值
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "----" + value);
        }
    }

推荐使用 keySet方式(键找值)。

3.3.3 HashMap判断键的唯一性

HashMap保证唯一性(键)的方式和HashSet是一模一样,因为HashSet内部就是在使用HashMap保存数据。

判断唯一性的方式:

  • 1.先比较两个对象的哈希值。
    如果对象的哈希值不同,肯定是不同的对象
    如果对象的哈希值相同,不一定是同一个对象
  • 2.然后比较两个对象的equals方法
    如果equals方法结果是true,表示两个对象相同
    如果equals方法结果是false,表示两个对象不同

如果HashMap要保证键的唯一性(属性相同就看成是同一个对象),需要同时重写hashCode和equals方法。

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    get..set..构造方法...toString
}
public static void main(String[] args) {
        //创建Map集合,键是学生对象,值家庭住址
        Map<Student, String> map = new HashMap<>();

        //向Map集合中添加元素
        map.put(new Student("刘备", 18), "蜀国");
        map.put(new Student("曹操", 16), "魏国");
        map.put(new Student("孙权", 20), "吴国");

        //添加
        map.put(new Student("刘备", 20), "巴蜀");

        //遍历Map集合,输出里面的每一个键和每一个值
        //调用keySet方法,获取到Map集合中所有的键
        Set<Student> keys = map.keySet();
        //遍历Set集合,拿到里面的每一个键
        for (Student key : keys) {
            //根据键获取对应的值
            String value = map.get(key);
            System.out.println(key + "---------------" + value);
        }
    }

3.3.4 LinkedHashMap

LinkedHashMap是Map接口下一个不常用的实现类,内部除了有一个哈希表之外还有一个链表,链表可以保证有序。

public static void main(String[] args) {
        //创建Map集合
        Map<String, String> map = new LinkedHashMap<>();

        map.put("it001", "张三");
        map.put("it002", "李四");
        map.put("it003", "王五");

        System.out.println(map);//{it001=张三, it002=李四, it003=王五}
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值