Loading

Java学习笔记 -集合2

鉴于HashSet、TreeSet和Map集合中的HashMap、TreeMap关联,所以放在一起
主要内容:
1.TreeSet、TreeMap、比较器
2.HashSet、HashMap、哈希表

继承结构图

Map接口

1.概述
  • Map和Collection没有继承关系
  • Map集合以key和value的方式存储数据:键值对
  • key和value都是引用数据类型
  • key和value都是存储对象的内存地址
  • key起到主导地位,value是key的一个附属品
2.常用方法:
  • V put(K key, V value) 向集合中添加键值对

  • V get(Object key) 通过key 获取value

  • void clear() 清空Map集合

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

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

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

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

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

  • V remove(Object key) 通过key删除键值对

  • int size() 获取Map集合这键值对的个数

  • Set<Map.Entry<K,V>> entrySet() 将Map集合转换成Set集合

  • 以及Map.Entry静态内部类中的实例方法:

  • K getKey() 获取key

  • V getValue() 获取value
    Map集合通过entrySet()方法转换成成的这个Set集合,Set集合中元素的类型是Map.Entry(静态内部类)

例如:
HashMap<Integer,String> map1 = new HashMap<>;
key       value
1          zhangsan
2          lisi

Set<Map.Entry<Integer,String>> s = map1.entrySet();

则s集合对象如下:
1=zhangsan    
2=lisi

示例程序

public class MapTest01 {
    public static void main(String[] args) {
        //创建Map对象
        Map<Integer, String> map = new HashMap<>();
        map.put(1,"zhagnsan");
        map.put(2,"lisi");

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

        //判断集合是否包含某个key
        System.out.println(map.containsKey(1));   //true

        //判断集合是否包含某个value
        System.out.println(map.containsValue("lisi"));  //true

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

        //获取Map集合所有的value(所有的值是一个Collection集合)
        Collection<String> collection = map.values();
        for(String str : collection){
            System.out.println(str);
        }

        //遍历Map的第一种方式 : KeySet + get方法来遍历键值对
        for(Integer integer : set){
            System.out.println(integer + "=" + map.get(integer));
        }

        //遍历Map的第二种方式 :  Set<Map.Entry<K,V>> entrySet()
        //推荐第二种方式,第二种方式的效率高
        Set<Map.Entry<Integer,String>> set1 = map.entrySet();

        Iterator<Map.Entry<Integer, String>> it2 = set1.iterator();
        while(it2.hasNext()){
            Map.Entry<Integer, String> node = it2.next();
            Integer key = node.getKey();
            String value1 = node.getValue();
            System.out.println(key + "=" + value1);
        }
        //foreach
        for(Map.Entry<Integer,String> node : set1){
            Integer key = node.getKey();
            String value1 = node.getValue();
            System.out.println(key + "=" + value1);
        }
    }
}

如何理解静态内部类?

可以通过以下程序类比:

public class StaticInClassTest {

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

        public void m2(){
            System.out.println("静态内部类的实例方法");
        }
    }

    public static void main(String[] args) {
        StaticInClassTest.InnerClass mi = new StaticInClassTest.InnerClass();
        mi.m2();

        //Set集合中存储的StaticInClassTest.InnerClass类型
        Set<StaticInClassTest.InnerClass> s = new HashSet<>();
    }
}

TreeMap & TreeSet

1.概述

  • TreeSet集合底层实际上是一个TreeMap
  • TreeMap集合底层是一个二叉树
  • 放在TreeSet集合的元素,等同于放在了TreeMap集合的key部分
  • treeSet集合中的元素:无序不可重复,但可以按照元素的大小顺序自动排序,称为:可排序集合

2.TreeSet TreeMap可以自动排序的原理:

部分源码:

// 在java.util.TreeMap中
Comparator<? super K> cpr = comparator;
 if (cpr != null) {         //比较器分支
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);  //直接调用比较器的compare方法
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {           //Comparable接口分支
            if (key == null)
                //先强制类型转换之后,在调用compareTo方法
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
  • 所以,如果需要使用TreeSet&TreeMap存储自定义的类,需要实现Comparable接口或者编写比较器类实现Comparator接口
Comparable接口和Comparator接口如何选择?
  • Comprable接口是强制类型转换后,调用comparaTo方法,所以它的比较规则是固定的(Integer & String均是选择这种方法)
  • 如果比较规则有多个,需要频繁切换,那么建议编写多个比较器,在不同情况下传递不同的比较器;(还可以使用匿名内部类的语法)

示例程序:

public class TreeMapTest01 {
    public static void main(String[] args) {
        //Comparable接口的方式:
        //如果没有实现接口Comparable,会报类型转换异常:java.lang.ClassCastException
        TreeSet<Person> people = new TreeSet<>();
        Person p1 = new Person("zs",20);
        Person p2 = new Person("ls",12);
        Person p3 = new Person("zd",20);

        people.add(p1);
        people.add(p2);
        people.add(p3);
        for(Person p : people){
            System.out.println(p);
        }

        //===================================================
        //Comparator接口,编写一个类的方式实现Comparator接口
        //有参数构造方法:TreeSet(Comparator<? super E> comparator)   传递比较器
        TreeSet<Person> people1 = new TreeSet<>(new PerSonComparator());
        people1.add(p1);
        people1.add(p2);
        people1.add(p3);
        for(Person p : people1){
            System.out.println(p);
        }

        //=================================================
        //Comparator接口,使用匿名内部类的语法实现Comparator接口
        TreeSet<Person> people2 = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.age - o2.age;
            }
        });
    }
}

//Comparable接口是java.lang下的
class Person implements Comparable<Person>{
    String name;
    int age;

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


    //compareTo方法的返回值
    //返回值相同,value会覆盖,返回值>0,会在右子树上找
    @Override
    public int compareTo(Person o) {
        if(this.age == o.age){
            return this.name.compareTo(o.name);
        }else {
            return this.age - o.age;
        }
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

//编写类实现java.util.Comparator接口。
class PerSonComparator implements Comparator<Person>{
    @Override
    public int compare(Person o1, Person o2) {
        if(o1.age == o2.age){
            return o1.name.compareTo(o2.name);
        }else {
            return o1.age - o2.age;
        }
    }
}

HashSet、HashMap、哈希表

1.概述

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

  • 哈希表是一个数组和单向链表的结合体
    -hashMap集合的默认初始化容量是16,默认加载因子是0.75(数组使用75%时就开始扩容)扩容为二倍

  • (重点)HashMap集合初始化容量必须是2的倍数,这是因为达到散列均匀,为了提高HashMap集合的存取效率所必需的

2.HashMap集合底层的源代码:

public class HashMap{
    //HashMap底层实际上是一个数组
    Node<K,V>[] table;

    //静态内部类HashMap.Node
    static class Node<K,V>{
        final int hash;  //哈希值
        final K key;
        V value;
        Node<K,V> next; //next对象
        }
    }

3.两个重要方法的原理(图示):

  • map.put(k,v)
  • v = map.get(k)

5.hashCode()

如果hashCode()方法返回值为某个固定值,会导致哈希表变成一个单向链表
如果hashCode()方法返回值均不相同,会导致哈希表变成一个一维数组
这些情况都称之为: 散列分布不均匀
要想实现散列分布均匀,需要重写hashCode()方法有一定的技巧

6.(重点)HashMap的Key类和HashSet集合中的元素的equals & hashCode方法

  • 如果一个类的equals方法重写,那么hashCode()方法必须重写
  • 且如果equals方法显示两个对象相同,那么他们hashCode()方法的返回值必须相同
  • hashCode equals方法可以直接使用IDEA生成,必须同时生成防止出错

示例程序:

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

        Map<Integer, String> map = new HashMap<>();

    }
}

class Usr {
    private String name;

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

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Usr)) return false;
        Usr usr = (Usr) o;
        return getName().equals(usr.getName());
    }
    @Override
    public int hashCode() {
        return Objects.hash(getName());
    }
}

属性类:Properties

  • 是一个Map集合,继承Hashtale,它的key和value都是String类型
  • 也被称之为属性类对象 是线程安全的

示例程序

import java.util.Properties;

public class PropertiesTest01 {
    public static void main(String[] args) {
        Properties pro = new Properties();

        //需要掌握两个方法
        pro.setProperty("a","ddddf");
        pro.setProperty("b","dfbgdfd");

        //通过key获取value
        System.out.println(pro.getProperty("a"));
        System.out.println(pro.getProperty("b"));
    }
}
posted @ 2020-06-01 20:56  Krocz  阅读(144)  评论(0编辑  收藏  举报