JavaSE 进阶 - 第22章 集合(3)

0、Map接口

1、Map和Collection没有关系!
2、Map集合以key和value的方式存储数据:键值对
  key和value都是引用数据类型。
  key和value都是存储对象的内存地址。
  key起到主导的地位,value是key的一个附属品。
3、Map集合的key部分特点:无序不可重复。(Map集合的key部分 和 Set集合 存储元素特点相同
在这里插入图片描述

1、掌握Map接口中常用方法

  1. V put(K key, V value)———— 向Map集合中添加键值对

  2. V get(Object key)———— 通过key获取value

  3. int size()———— 获取Map集合中键值对的个数。

  4. V remove(Object key)———— 通过key删除键值对

  5. boolean containsKey(Object key)———— 判断Map中是否包含某个key

  6. boolean containsValue(Object value)———— 判断Map中是否包含某个value

  7. Collection values()———— 获取Map集合中所有的value,返回一个Collection

  8. Set keySet()———— 获取Map集合所有的key(所有的键是一个set集合)

  9. void clear()———— 清空Map集合

  10. boolean isEmpty()———— 判断Map集合中元素个数是否为0

  11. Set<Map.Entry<K,V>> entrySet()———— 将Map集合转换成Set集合
    【对第11个方法的理解】:

       假设现在有一个Map集合,如下所示:
           map1集合对象
           key             value
           ----------------------------
           1               zhangsan
           2               lisi
           3               wangwu
           4               zhaoliu
    
           Set set = map1.entrySet();
    
           set集合对象:
           1=zhangsan 【注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是 Map.Entry<K,V>】
           2=lisi     【Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,Entry是Map中的静态内部类】
           3=wangwu    
           4=zhaoliu ---> 这个东西是一个整体、是一个元素,这个东西的类型是:Map.Entry
    

    【静态内部类在之前18章涉及过,这里可以参看MyClass.java】

import java.util.HashSet;
import java.util.Set;

public class MyClass {

    // 声明一个静态内部类
    private static class InnerClass {
        // 静态方法
        public static void m1(){
            System.out.println("静态内部类的m1方法执行");
        }
        // 实例方法
        public void m2(){
            System.out.println("静态内部类中的实例方法执行!");
        }
    }

    public static void main(String[] args) {

        // 类名叫做:MyClass.InnerClass
        MyClass.InnerClass.m1();

        // 创建静态内部类对象
        MyClass.InnerClass mi =  new MyClass.InnerClass();
        mi.m2();

        // 给一个Set集合
        // 该Set集合中存储的对象是:MyClass.InnerClass类型
        Set<MyClass.InnerClass> set = new HashSet<>();

        // 这个Set集合中存储的是字符串对象。
        Set<String> set2 = new HashSet<>();

        Set<MyMap.MyEntry<Integer, String>> set3 = new HashSet<>();
    }
}

class MyMap {
    public static class MyEntry<K,V> {

    }
}
/*
静态内部类的m1方法执行
静态内部类中的实例方法执行!
 */		

【对Map中常用11个方法的应用示例】:

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

public class MapTest01 {
    public static void main(String[] args) {
        // 创建Map集合对象
        Map<Integer, String> map = new HashMap<>();

        // 1、 向Map集合中添加键值对
        map.put(1, "zhangsan"); // 1在这里进行了自动装箱。
        map.put(2, "lisi");
        map.put(3, "wangwu");
        map.put(4, "zhaoliu");

        // 2、 通过key获取value
        String value = map.get(2);
        System.out.println(value);

        // 3、 获取键值对的数量
        System.out.println("键值对的数量:" + map.size());

        // 4、 通过key删除key-value
        map.remove(2);
        System.out.println("键值对的数量:" + map.size());

        // 5、 判断是否包含某个key
        // contains方法底层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法。
        System.out.println(map.containsKey(new Integer(4))); // true

        // 6、 判断是否包含某个value
        System.out.println(map.containsValue(new String("wangwu"))); // true

        // 7、 获取所有的value
        Collection<String> values = map.values();
        // foreach
        for(String s : values){
            System.out.println(s);
        }

        // 8、获取Map集合所有的key(所有的键是一个set集合)
        Set<Integer> key = map.keySet();
        for (Integer k : key) {
            System.out.println(k);
        }

        // 11、 将Map集合转换成Set集合
        Set<Map.Entry<Integer,String>> set =  map.entrySet();
        for (Map.Entry<Integer,String> s : set) {
            System.out.println(s);
        }

        // 9、 清空map集合
        map.clear();
        System.out.println("键值对的数量:" + map.size());

        // 10、 判断是否为空
        System.out.println(map.isEmpty()); // true
    }
}
/*
lisi
键值对的数量:4
键值对的数量:3
true
true
zhangsan
wangwu
zhaoliu
1
3
4
1=zhangsan
3=wangwu
4=zhaoliu
键值对的数量:0
true
 */

2、遍历Map集合的两种方式

  • 第一种:获取所有key,遍历每个key,通过key获取value.

  • 第二种:使用这个方法Set<Map.Entry<K,V>> entrySet()
        遍历Set集合中的node,调用node.getKey(), node.getValue()

    第二种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值。
    比较适合于大数据量。

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

/*
Map集合的遍历。【非常重要】
 */
public class MapTest02 {
    public static void main(String[] args) {
        // 第一种方式:获取所有的key,通过遍历key,来遍历value
        Map<Integer, String> map = new HashMap<>();
        map.put(1,"1号");
        map.put(2,"2号");
        map.put(3,"3号");
        map.put(4,"4号");
        // 遍历Map集合
        // 获取所有的key,所有的key是一个Set集合
        Set<Integer> keys = map.keySet();
        // 遍历key,通过key获取value

 /*       // 迭代器可以
        Iterator<Integer> it = keys.iterator();
        while(it.hasNext()){
            // 取出其中一个key
            Integer key = it.next();
            // 通过key获取value
            String value = map.get(key);
            System.out.println("key="+key+",value="+value);
        }
 */
        // foreach也可以
        for(Integer key : keys){
            System.out.println("key="+key+",value="+map.get(key));
        }

        // 第二种方式:Set<Map.Entry<K,V>> entrySet()
        // 以上这个方法是把Map集合直接全部转换成Set集合。
        // Set集合中元素的类型是:Map.Entry
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        // 遍历Set集合,每一次取出一个Node
        // 迭代器
        /*Iterator<Map.Entry<Integer,String>> it2 = set.iterator();
        while(it2.hasNext()){
            Map.Entry<Integer,String> node = it2.next();
            Integer key = node.getKey();
            String value = node.getValue();
            System.out.println(key + "=" + value);
        }*/

        // foreach
        // 这种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值。
        // 这种方式比较适合于大数据量。
        for(Map.Entry<Integer,String> node : set){
            System.out.println(node.getKey() + "--->" + node.getValue());
        }
    }
}
/*
key=1,value=1号
key=2,value=2号
key=3,value=3号
key=4,value=4号
1--->1号
2--->2号
3--->3号
4--->4号
 */

在这里插入图片描述

3、HashMap集合、哈希表数据结构

1、HashMap集合底层是哈希表/散列表的数据结构。

2、哈希表是一个怎样的数据结构呢?
    哈希表是一个数组和单向链表的结合体
    数组:在查询方面效率很高,随机增删方面效率很低。
    单向链表:在随机增删方面效率较高,在查询方面效率很低。
    哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。

3、HashMap集合底层的源代码:

public class HashMap{
    // HashMap底层实际上就是一个数组。(一维数组)
    Node<K,V>[] table;
    // 静态的内部类HashMap.Node
    static class Node<K,V> {
		final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)
		final K key; // 存储到Map集合中的那个key
		V value; // 存储到Map集合中的那个value
		Node<K,V> next; // 下一个节点的内存地址。
    }
}

    哈希表/散列表一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)

4、最主要掌握的是:
    map.put(k,v)——放
    v = map.get(k)——取
    以上这两个方法的实现原理,是必须掌握的。

在这里插入图片描述
5、HashMap集合的key部分特点:
  无序,不可重复
  为什么无序?
    因为不一定挂到哪个单向链表上。
  不可重复是怎么保证的?
    equals方法来保证HashMap集合的key不可重复。
    如果key重复了,value会覆盖。

  放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
  所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法

6、哈希表HashMap使用不当时无法发挥性能!
  假设将所有的hashCode()方法返回值固定为某个值(所有下标相同),那么会导致底层哈希表变成了 纯单向链表。
  这种情况我们成为:散列分布不均匀

  什么是散列分布均匀
    假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。

  假设将所有的hashCode()方法返回值都设定为不一样的值(所有下标不同),可以吗,有什么问题?
    不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。

  散列分布均匀需要你重写hashCode()方法时有一定的技巧。

7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法

8、HashMap集合的默认初始化容量是16,默认加载因子是0.75
  这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。

  重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
  这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

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

public class HashMapTest01 {
    public static void main(String[] args) {
        // 测试HashMap集合key部分的元素特点:无序不可重复
        // Integer是key,它的hashCode和equals都重写了。
        Map<Integer,String> map = new HashMap<>();
        map.put(1111, "zhangsan");
        map.put(6666, "lisi");
        map.put(7777, "wangwu");
        map.put(2222, "zhaoliu");
        map.put(2222, "king"); //key重复的时候value会自动覆盖。

        System.out.println(map.size()); // 4

        // 遍历Map集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        for(Map.Entry<Integer,String> entry : set){
            // 验证结果:HashMap集合key部分元素:无序不可重复。
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}
/*
4
7777=wangwu
1111=zhangsan
6666=lisi
2222=king
 */

4、存放在HashMap集合key部分和HashSet集合中的元素需要同时重写hashCode和equals

  • 4.0、为什么重写equals方法就得重写hashCode方法?

      equals方法重写,为了来比较HashMap集合的key的内容(值),而不是内存地址
      hashCode方法重写,是为了遵循原则:
      	equals比较对象的内容(值)相同,那么hashCode一定相等
      	hashCode如果相等的情况下,对象的内容不一定相等。
    
      如果不重写,可能导致因为hash值不同,把元素直接放进去集合了,但是内容相同,有违“无序不可重复”
    
      1.重写hashCode() 用于获得元素的存储位置【数组下标】。
      2.重写equals() 用于在两个元素的位置相同的时候 比较两个元素是否相等。
    
  • 4.1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!equals方法有可能调用,也有可能不调用。
      拿put(k,v)举例,什么时候equals不会调用?
        k.hashCode()方法返回哈希值,
        哈希值经过哈希算法转换成数组下标。
        数组下标位置上如果是null,equals不需要执行。
      拿get(k)举例,什么时候equals不会调用?
        k.hashCode()方法返回哈希值,
        哈希值经过哈希算法转换成数组下标。
        数组下标位置上如果是null,equals不需要执行。

  • 4.2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
    并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
       equals方法返回true表示两个对象相同,在同一个单向链表上比较。
      那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
      所以hashCode()方法的返回值也应该相同。

  • 4.3、hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。

  • 4.4、终极结论:
      放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

  • 4.5、对于哈希表数据结构来说:
       如果o1和o2的hash值相同,一定是放到同一个单向链表上。
      当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

  • 4.6、HashMap集合key部分允许 null
    但是注意:HashMap集合的key 是 null值只能有一个,因为key重复的话value是覆盖。

import java.util.Objects;

public class Student {
    private String name;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //hashcode
    //equals
//    public boolean equals(Object obj) {
//        if(obj == null || !(obj instanceof Student)) return false;
//        if(obj == this) return true;
//        Student s = (Student)obj;
//        if(this.name.equals(s.name))return true;
//        return false;
//    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
import java.util.HashSet;
import java.util.Set;

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

        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");

        // 重写equals方法之前是false
        //System.out.println(s1.equals(s2)); // false

        // 重写equals方法之后是true
        System.out.println(s1.equals(s2)); //true (s1和s2表示相等)

        System.out.println("s1的hashCode=" + s1.hashCode()); //-1432604525(重写hashCode前603742814)
        System.out.println("s2的hashCode=" + s2.hashCode()); //-1432604525(重写hashCode前1826771953 )

        // s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么往HashSet集合中放的话,
        // 按说只能放进去1个。(HashSet集合特点:无序不可重复)
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);
        //System.out.println(students.size()); // 2 这个结果按说应该是1. 但是结果是2.显然不符合HashSet集合存储特点。怎么办?重写hashCode方法
        System.out.println(students.size());//1 重写hashCode之后,结果是1
    }
}
/*
true
s1的hashCode=-1432604525
s2的hashCode=-1432604525
1
 */

5、HashMap和Hashtable的区别

  • Hashtable是Map接口下面的另一个实现类。
  • HashMap和Hashtable的区别:
HashMapHashtable
初始化容量16,扩容后是原容量的:2倍初始化容量11,扩容后是原容量的:2倍+1
非线程安全线程安全(方法都带有synchronized关键字)
key和value可以为nullkey和value都不能是null

6、属性类Properties类

  • 6.1、Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
  • 6.2、Properties被称为属性类对象
  • 6.3、Properties是线程安全的。
  • 6.4、常用两个方法,一个存,一个取。
      setProperty
      getProperty
import java.util.Properties;

public class PropertiesTest01 {
    public static void main(String[] args) {
        // 创建一个Properties对象
        Properties pro = new Properties();

        // 存
        pro.setProperty("username","zhangsan");
        pro.setProperty("password","123");

        //取 通过key获取value
        String user = pro.getProperty("username");
        String pwd = pro.getProperty("password");

        System.out.println(user);
        System.out.println(pwd);
    }
}
/*
zhangsan
123
 */

7、TreeSet集合(TreeMap集合的key部分)

  • 7.1、TreeSet集合底层实际上是一个TreeMap

  • 7.2、TreeMap集合底层是一个二叉树

  • 7.3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。

  • 7.4、TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。 称为:可排序集合。
       (1)无序:存储时顺序和取出的顺序不同,并且没有下标
       (2)不可重复:元素不可重复
       (3)可以按照元素的大小顺序自动排序:
       ————存进去的String类型的字符串,遍历时,输出的按照字典顺序,升序!
       ————存进去的Integer整数类型,遍历时,输出的按照大小,升序!

import java.util.TreeSet;

public class TreeSetTest02 {
    public static void main(String[] args) {
        // 创建一个TreeSet集合
        TreeSet<String> ts = new TreeSet<>();
        // 添加String
        ts.add("zhangsan");
        ts.add("lisi");
        ts.add("wangwu");
        ts.add("zhangsi");
        ts.add("wangliu");
        // 遍历
        for(String s : ts){
            // 按照字典顺序,升序!
            System.out.println(s);
        }

        TreeSet<Integer> ts2 = new TreeSet<>();
        ts2.add(100);
        ts2.add(200);
        ts2.add(900);
        ts2.add(800);
        ts2.add(600);
        ts2.add(10);
        for(Integer elt : ts2){
            // 升序!
            System.out.println(elt);
        }
    }
}

/*
lisi
wangliu
wangwu
zhangsan
zhangsi
10
100
200
600
800
900
 */
  • 7.5、TreeSet集合到底有什么用处?

      数据库中有很多数据:
      userid  name     birth
      -------------------------------------
      1       zs          1980-11-11
      2       ls          1980-10-11
      3       ww          1981-11-11
      4       zl          1979-11-11
    
      编写程序从数据库当中取出数据,在页面展示用户信息的时候按照生日升序或者降序。
      这个时候可以使用TreeSet集合,因为TreeSet集合放进去,拿出来就是有顺序的。
    
  • 7.6、TreeSet集合无法对自定义类型排序

      以下程序中对于Person类型来说,无法排序。
      	因为没有指定Person对象之间的比较规则。谁大谁小并没有说明啊。
      运行时出现了这个异常:
      java.lang.ClassCastException:
          class com.yuming.javase.collection.Person
          cannot be cast to class java.lang.Comparable
      出现这个异常的原因是:
      	Person类没有实现java.lang.Comparable接口。
      	而之前的String类和Integer类在创建时已经实现了Comparable接口,所以可以自动排序
    
package com.yuming.javase.collection;

import java.util.TreeSet;

public class TreeSetTest03 {
    public static void main(String[] args) {
        Person p1 = new Person(32);
        //System.out.println(p1);
        Person p2 = new Person(20);
        Person p3 = new Person(30);
        Person p4 = new Person(25);

        // 创建TreeSet集合
        TreeSet<Person> persons = new TreeSet<>();
        // 添加元素
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(p4);

        // 遍历
        for (Person p : persons){
            System.out.println(p);
        }
    }
}

class Person {
    int age;
    public Person(int age){
        this.age = age;
    }

    // 重写toString()方法
    public String toString(){
        return "Person[age="+age+"]";
    }
}
自定义类型想要排序,可以实现Comparable接口

【示例1】:

import java.util.TreeSet;

public class TreeSetTest04 {
    public static void main(String[] args) {
        Customer c1 = new Customer(32);
        Customer c2 = new Customer(20);
        Customer c3 = new Customer(30);

        // 创建TreeSet集合
        TreeSet<Customer> customers = new TreeSet<>();
        // 添加元素
        customers.add(c1);
        customers.add(c2);
        customers.add(c3);

        // 遍历
        for (Customer c : customers){
            System.out.println(c);
        }
    }
}

// 放在TreeSet集合中的元素需要实现java.lang.Comparable接口
// 并且实现compareTo方法。equals可以不写。
class Customer implements Comparable<Customer>{
    int age;

    public Customer(int age){
        this.age = age;
    }
    public String toString(){
        return "Customer[age="+age+"]";
    }
    // 需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
    // k.compareTo(t.key)
    // 拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 =0
    // 比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。
    @Override
    public int compareTo(Customer c) { // c1.compareTo(c2);
        // this是c1,c是c2
        // c1和c2比较的时候,就是this和c比较。

        /*int age1 = this.age;
        int age2 = c.age;
        if(age1 == age2){
            return 0;
        } else if(age1 > age2) {
            return 1;
        } else {
            return -1;
        }*/

        //以上代码可简化:
        return this.age - c.age; // =0 >0 <0    升序
        //return c.age - this.age;             // 降序
    }
}
/*
输出结果:
Customer[age=20]
Customer[age=30]
Customer[age=32]
 */

【示例2】:

import java.util.TreeSet;

/*
先按照年龄升序,如果年龄一样的再按照姓名升序。
 */
public class TreeSetTest05 {
    public static void main(String[] args) {
        TreeSet<Vip> vips = new TreeSet<>();
        vips.add(new Vip("zhangsi", 20));
        vips.add(new Vip("zhangsan", 20));
        vips.add(new Vip("king", 18));
        vips.add(new Vip("soft", 17));
        for(Vip vip : vips){
            System.out.println(vip);
        }
    }
}

class Vip implements Comparable<Vip>{
    String name;
    int age;
    
    public Vip(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Vip{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /*
    compareTo方法的返回值很重要:
        返回0表示相同,value会覆盖。
        返回>0,会继续在右子树上找。【10 - 9 = 1 ,1 > 0的说明左边这个数字比较大。所以在右子树上找。】
        返回<0,会继续在左子树上找。
     */
    @Override
    public int compareTo(Vip v) {
        // 写排序规则,按照什么进行比较。
        if(this.age == v.age){
            // 年龄相同时按照名字排序。
            // 姓名是String类型,可以直接比。调用compareTo来完成比较。
            return this.name.compareTo(v.name);
        } else {
            // 年龄不一样
            return this.age - v.age;
        }
    }
}
/*
Vip{name='soft', age=17}
Vip{name='king', age=18}
Vip{name='zhangsan', age=20}
Vip{name='zhangsi', age=20}
 */
	compareTo方法的返回值很重要:
	   返回0表示相同,value会覆盖。
	   返回>0,会继续在右子树上找。【10 - 9 = 1 ,1 > 0的说明左边数字(要存放的元素)比较大。所以左边数字再到右子树上找、去比较】
	   返回<0,会继续在左子树上找。

8、自平衡二叉树 数据结构 (了解)

在这里插入图片描述
  存储原则:左小右大
  遍历方式:中序遍历(左根右)

9、TreeMap的key或者TreeSet集合中的元素要想排序,有两种实现方式

  • 第一种:实现java.lang.Comparable接口上面已讲

  • 第二种:实现java.util.Comparator(比较器)接口
        方法一:单独编写一个比较器的类,比较器实现java.util.Comparator接口
        方法二:使用匿名内部类的方式(这个类没有名字。直接new接口。)

【示例】:

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetTest06 {
    public static void main(String[] args) {
        // 创建TreeSet集合的时候,需要传一个比较器。

        // TreeSet<WuGui> wuGuis = new TreeSet<>();//这样不行,没有通过构造方法传递一个比较器进去。

        // 给构造方法传递一个比较器。【方法一代码片段】
        //TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator());

        // 大家也可以使用匿名内部类的方式(这个类没有名字。直接new接口。)【方法二代码片段】
        TreeSet<WuGui> wuGuis = new TreeSet<>(new Comparator<WuGui>() {
            @Override
            public int compare(WuGui o1, WuGui o2) {
                return o1.age - o2.age;
            }
        });

        wuGuis.add(new WuGui(1000));
        wuGuis.add(new WuGui(800));
        wuGuis.add(new WuGui(810));

        for(WuGui wuGui : wuGuis){
            System.out.println(wuGui);
        }
    }
}

// 乌龟
class WuGui{

    int age;

    public WuGui(int age){
        this.age = age;
    }

    @Override
    public String toString() {
        return "小乌龟[" +"age=" + age +']';
    }
}

// 单独在这里编写一个比较器【方法一代码片段】
// 比较器实现java.util.Comparator接口。(Comparator是java.util包下的。而Comparable是java.lang包下的。)
/*
class WuGuiComparator implements Comparator<WuGui> {

    @Override
    public int compare(WuGui o1, WuGui o2) {
        // 指定比较规则
        // 按照年龄排序
        return o1.age - o2.age;
    }
}
 */

/*
小乌龟[age=800]
小乌龟[age=810]
小乌龟[age=1000]
 */
  • Comparable和Comparator怎么选择呢?
      (1)当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口
      (2)如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口
      Comparator接口的设计符合OCP原则(对扩展开放,对修改关闭。)。

    另外:String类和Integer类在创建时已经实现了Comparable接口。

10、集合工具类Collections

  • 注:java.util.Collections 是集合工具,方便集合的操作
      java.util.Collection 是集合接口,下面有List集合接口、Set集合接口

  • Collections的其中两个方法:
        1、Collections.synchronizedList(list); // 将非线程安全的集合变成线程安全的
        2、Collections.sort(list); // 将list集合中的元素排序(要求集合中元素已实现Comparable接口。)

  • 通过集合工具类Collections,我们不只是TreeMap的key或者TreeSet集合中的元素可以排序了,List集合和Set集合也可以排序了
      (要求集合中元素已实现Comparable接口,另外Set集合需要先转换成List集合才能再排序)


import java.util.*;

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

        // ArrayList集合不是线程安全的。
        List<String> list = new ArrayList<>();

        // 第一个方法: 变成线程安全的
        Collections.synchronizedList(list);


        list.add("abf");
        list.add("abx");
        list.add("abc");
        list.add("abe");

        //第二个方法: 排序
        Collections.sort(list);

        for(String s : list){
            System.out.println(s);
        }

        System.out.println("——————我是分割线——————");

        List<WuGui2> wuGuis = new ArrayList<>();
        wuGuis.add(new WuGui2(1000));
        wuGuis.add(new WuGui2(8000));
        wuGuis.add(new WuGui2(500));
        // 注意:对List集合中元素排序,需要保证List集合中的元素实现了:Comparable接口。
        Collections.sort(wuGuis);
        for(WuGui2 wg : wuGuis){
            System.out.println(wg);
        }

        System.out.println("——————我是分割线——————");

        // 对Set集合怎么排序呢?
        Set<String> set = new HashSet<>();
        set.add("king");
        set.add("kingsoft");
        set.add("king2");
        set.add("king1");
        // 将Set集合转换成List集合
        List<String> myList = new ArrayList<>(set);
        Collections.sort(myList);
        for(String s : myList) {
            System.out.println(s);
        }

        // 这种方式也可以排序。
        //Collections.sort(list集合, 比较器对象);
    }
}

//自定义类型,排序,这里实现Comparable接口
class WuGui2 implements Comparable<WuGui2>{
    int age;
    public WuGui2(int age){
        this.age = age;
    }
    //重写compareTo方法
    @Override
    public int compareTo(WuGui2 o) {
        return this.age - o.age;
    }

    @Override
    public String toString() {
        return "WuGui2{" + "age=" + age +'}';
    }
}

/*
abc
abe
abf
abx
——————我是分割线——————
WuGui2{age=500}
WuGui2{age=1000}
WuGui2{age=8000}
——————我是分割线——————
king
king1
king2
kingsoft
 */

【总结——集合】:集合这块最主要掌握什么内容?

1、每个集合对象的创建(new)
2、向集合中添加元素
3、从集合中取出某个元素
4、遍历集合

5、主要的集合类:
	ArrayList
	LinkedList
	HashSet (同HashMap的key,存储在HashMap集合key的元素需要同时重写hashCode + equals)
	TreeSet
	HashMap (存储在HashMap集合key的元素需要同时重写hashCode + equals)
	Properties
	TreeMap
1、【ListTest.java】—ArrayList、LinkedList
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

/*
	1、每个集合对象的创建(new)
	2、向集合中添加元素
	3、从集合中取出某个元素
	4、遍历集合
 */
public class ListTest {
    public static void main(String[] args) {
        //1、每个集合对象的创建(new)
        //ArrayList<String> list = new ArrayList<>();  // ArrayList集合
        LinkedList<String> list = new LinkedList<>();  // LinkedList集合
        //2、向集合中添加元素
        list.add("张三");
        list.add("lisi");
        list.add("wangwu");
        //3、从集合中取出某个元素  (List集合有下标)
        String s1 = list.get(0);
        System.out.println(s1);          //  张三
        System.out.println(list.get(1)); //  lisi
        //4、遍历集合
        //遍历1(迭代器方式,是所有Collection通用的一种方式)
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        //遍历2(下标方式)(List集合有下标)
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //遍历3(增强for循环)
        for (String  s : list) {
            System.out.println(s);
        }
        // 遍历4: while循环修改为for循环
        for(Iterator<String> it2 = list.iterator(); it2.hasNext(); ){
            System.out.println("====>" + it2.next());
        }
    }
}
/*
张三
lisi
张三
lisi
wangwu
张三
lisi
wangwu
张三
lisi
wangwu
====>张三
====>lisi
====>wangwu
 */
2、【HashSetTest.java】—HashSet
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;

/*
	1、每个集合对象的创建(new)
	2、向集合中添加元素
	3、从集合中取出某个元素
	4、遍历集合
	5、测试HashSet集合的特点:无序不可重复。
	6、HashSet ,实际上是在HashMap的key部分(存储在HashMap集合key的元素需要同时重写hashCode + equals)
 */
public class HashSetTest {
    public static void main(String[] args) {
        // 1、每个集合对象的创建(new)
        HashSet<String> set = new HashSet<>();
        // 2、向集合中添加元素
        set.add("abc");
        set.add("def");
        set.add("king");

        // 3、从集合中取出某个元素
        // set集合中的元素不能通过下标取了。没有下标。取不出某个元素

        // 4、遍历集合
        //遍历1(迭代器方式,是所有Collection通用的一种方式)
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        //遍历2(增强for循环)
        for (String  s : set) {
            System.out.println(s);
        }
        //5、测试HashSet集合的特点:无序不可重复。
        set.add("king");
        set.add("king");
        System.out.println(set.size()); //3 (不可重复--后面2个king都没有加进去。)
        set.add("1");
        set.add("10");
        set.add("2");
        for (String  s : set) {
            System.out.println("----->"+s);// (无序--遍历出来的和存进去的顺序不一样)
        }

        //6、HashSet ,实际上是在HashMap的key部分(存储在HashMap集合key的元素需要同时重写hashCode + equals)

        HashSet<Student> students = new HashSet<>();
        Student s1 = new Student(111,"zhangsan");
        Student s2 = new Student(222,"lisi");
        Student s3 = new Student(111,"zhangsan");
        students.add(s1);
        students.add(s2);
        students.add(s3);
        System.out.println(students.size());  // 2  不可重复,s3没有添加进去

        // 遍历
        for(Student stu : students){
            System.out.println(stu);
        }
    }
}

class Student{
    int no;
    String name;

    public Student() {
    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return no == student.no &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(no, name);
    }
}
/*
abc
def
king
abc
def
king
3
----->1
----->2
----->abc
----->def
----->king
----->10
2
Student{no=222, name='lisi'}
Student{no=111, name='zhangsan'}
 */
3、【TreeSetTest.java】—TreeSet、 TreeMap
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

/*
    1、每个集合对象的创建(new)
	2、向集合中添加元素
	3、从集合中取出某个元素
	4、遍历集合
	5、测试TreeSet集合中的元素是可排序的。
	6、测试TreeSet集合中存储的类型是自定义的。
	7、第一种方式:测试实现Comparable接口的方式
	8、第二种方式:测试实现Comparator接口的方式
		8.1 方法一:单独编写一个比较器的类,比较器实现java.util.Comparator接口
		8.2 方法二:使用匿名内部类的方式(这个类没有名字。直接new接口。)

 */
public class TreeSetTest {
    public static void main(String[] args) {
        //1、集合的创建(可以测试以下TreeSet集合中存储String、Integer的。这些类都是SUN写好的。)
        //TreeSet<Integer> ts = new TreeSet<>();

        // 5、可以编写比较器可以改变规则(让遍历结果降序)。重写比较器
        TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1; // 自动拆箱(把Integer拆成数字)
            }
        });

        //2、向集合中添加元素
        ts.add(1);
        ts.add(100);
        ts.add(10);
        ts.add(10);
        ts.add(10);
        ts.add(10);
        ts.add(0);

        // 3、从集合中取出某个元素
        // set集合中的元素不能通过下标取了。没有下标。取不出某个元素

        // 4、遍历集合
        //遍历1(迭代器方式,是所有Collection通用的一种方式)
        Iterator<Integer> it = ts.iterator();
        while (it.hasNext()){
            System.out.println(it.next());   //5、结果是排序了的
        }
        //遍历2(增强for循环)
        for(Integer x : ts){
            System.out.println(x);   //结果是排序了的
        }

        //  6、TreeSet集合中存储自定义类型
        //  7、A类创建时已经在下面实现Comparable接口
        TreeSet<A> atree = new TreeSet<>();
        atree.add(new A(1000));
        atree.add(new A(200));
        atree.add(new A(500));
        atree.add(new A(300));
        atree.add(new A(400));
        atree.add(new A(100));
        // 遍历
        for(A a : atree){
            System.out.println(a);
        }

        // 8.1 给构造方法传递自己在下面写的比较器:BComparator
        TreeSet<B> btree = new TreeSet<>(new BComparator());

//        // 8.2 匿名内部类方式
//        TreeSet<B> btree = new TreeSet<>(new Comparator<B>() {
//            @Override
//            public int compare(B o1, B o2) {
//                return o1.i - o2.i;
//            }
//        });

        btree.add(new B(500));
        btree.add(new B(1000));
        btree.add(new B(2000));
        btree.add(new B(600));
        btree.add(new B(300));
        btree.add(new B(5));
        for(B b : btree){
            System.out.println(b);
        }
    }
}
// 7、第一种方式:实现Comparable接口
class A  implements  Comparable<A>{
    int i;
    public A() { }
    public A(int i) { this.i = i; }

    @Override
    public String toString() { return "A{" + "i=" + i + '}'; }

    @Override
    public int compareTo(A o) {
        //return this.i - o.i; //升序
        return o.i - this.i;   //降序
    }
}

class B{
    int i;
    public B(int i) { this.i = i; }

    @Override
    public String toString() { return "B{" + "i=" + i + '}'; }
}
//8.1 单独编写一个比较器的类,比较器实现java.util.Comparator接口
class BComparator implements  Comparator<B>{
    @Override
    public int compare(B o1, B o2) {
        return o1.i - o2.i;
    }
}

/*
100
10
1
0
100
10
1
0
A{i=1000}
A{i=500}
A{i=400}
A{i=300}
A{i=200}
A{i=100}
B{i=5}
B{i=300}
B{i=500}
B{i=600}
B{i=1000}
B{i=2000}
 */
4、【HashMapTest.java】—HashMap
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
    1、每个集合对象的创建(new)
	2、向集合中添加元素(所有的Map集合的key部分 同 Set集合特点:无序不可重复 )
	3、从集合中取出某个元素
	4、遍历集合
		第一种:获取所有key,遍历每个key,通过key获取value.
	    第二种:使用这个方法Set<Map.Entry<K,V>> entrySet() ,把Map集合直接全部转换成Set集合
	        遍历Set集合中的node,调用node.getKey(), node.getValue()
 */
public class HashMapTest {
    public static void main(String[] args) {
        // 1、创建 HashMap 集合
        // Integer是key,它的hashCode和equals都已经重写了。
        Map<Integer, String> map = new HashMap<>();
        // 2、添加元素
        map.put(1, "zhangsan");
        map.put(9, "lisi");
        map.put(10, "wangwu");
        map.put(2, "king");
        map.put(2, "simth"); // key重复value会覆盖。
        System.out.println(map.size());  // 4
        // 3、取元素--取key是2的元素
        System.out.println(map.get(2)); //simth

        // 4、遍历Map集合很重要,几种方式都要会。
        // 第一种方式:先获取所有的key,遍历key的时候,通过key获取value
        Set<Integer> keys = map.keySet();
        for(Integer key : keys){
            System.out.println(key + "=" + map.get(key));
        }

        // 第二种:使用这个方法Set<Map.Entry<K,V>> entrySet() ,把Map集合直接全部转换成Set集合
        //	遍历Set集合中的node,调用node.getKey(), node.getValue()
        Set<Map.Entry<Integer,String>> nodes = map.entrySet();
        for (Map.Entry<Integer,String> node: nodes) {
            System.out.println(node.getKey()+"="+node.getValue());
        }
    }
}
/*
4
simth
1=zhangsan
2=simth
9=lisi
10=wangwu
1=zhangsan
2=simth
9=lisi
10=wangwu
 */
5、【PropertiesTest.java】—Properties
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) {
        //创建对象
        Properties pro = new Properties();
        //存
        pro.setProperty("username","zhangsan");
        pro.setProperty("password","123");
        //取
        String u= pro.getProperty("username");
        String p= pro.getProperty("password");
        System.out.println(u);
        System.out.println(p);
    }
}
/*
zhangsan
123
 */

传送门

上一章:JavaSE 进阶 - 第22章 集合(2)
下一章:JavaSE 进阶 - 第23章 IO流

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面向对象编程是一种编程范式,它将程序的构建和设计思路以面向对象的方式进行组织和实现。在Java中,面向对象编程是基于Java SE(Standard Edition)的一种编程方式。第07讲主要介绍了面向对象编程中的一些基本概念和关键术语。 在面向对象编程中,我们将程序中的数据和对数据的操作(方法)封装在一起,形成一个对象。对象由两部分构成:属性和方法。属性是用来描述对象的特征,而方法则是对象可以执行的操作。对象之间通过消息(方法调用)进行通信和交互。面向对象的核心思想是通过封装、继承和多态实现程序的复用和扩展。 封装是面向对象编程中的一个重要概念,它指的是将类的属性和方法进行封装,使得外部无法直接访问和修改对象的内部状态,只能通过公共的方法来操作属性和执行方法。封装提供了一种将数据和行为组合在一起的方式,可以保护数据的完整性和安全性。 继承是面向对象编程中的另一个重要概念,它指的是通过定义一个新的类来继承现有类的属性和方法。通过继承,子类可以继承父类的属性和方法,并可以在此基础上进行扩展和修改。继承提供了一种代码复用的机制,可以减少重复编码的工作量。 多态是面向对象编程的又一个重要概念,它指的是同一类型的对象在不同的情况下可以有不同的表现形式。多态通过方法的重写和方法的重载实现。方法的重写指的是在子类中重新定义和实现父类的方法,方法的重载指的是在同一个类中可以定义多个同名但参数列表不同的方法。 总结来说,面向对象编程是一种将程序组织和设计思路以对象为中心的编程方式。在JavaSE中,我们可以通过封装、继承和多态来实现面向对象编程的目标。封装可以提高程序的可维护性和可复用性,继承可以减少重复编码的工作量,多态可以灵活地操作对象。掌握这些基本概念和关键术语,可以帮助我们更好地理解和应用面向对象编程的思想。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值