Bootstrap

Java进阶第七章——数据结构:Map

数据结构:Map

本章所涉及到的数据结构知识可在数据结构学习记录中学习:
咖啡ice的数据结构学习记录

1.Map常用方法

  • Map和Collection没有继承关系。

  • Map集合以key和value的方式存储数量:键值对。

    key起主导地位,value是key的附属品。

  • Map接口中常用方法:

    Map支持泛型:Map<K,V>

    向Map中放键值对:V put(K key,V value)

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

    清空Map:void clear()

    判断是否包含某个key:boolean containsKey(Object key)

    判断是否包含某个value:boolean containsValue(Object value)

    判断集合是否为空:boolean isEmpty()

    获取集合中所有的key,所有的健是一个Set集合:Set<K> keySet()

    获取集合中所有的value:Collection<V> values()

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

    返回Map集合中键值个数:int size()

    将Map集合转化为Set集合:Set<Map.Entry<K,V>> entrySet()

public class MapTest {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<Integer,String> map = new HashMap<>();
        //向Map集合中添加键值对
        map.put(1,"ice");
        map.put(2,"coffee");
        map.put(3,"is");
        map.put(4,"nice");
        //通过key获取value
        System.out.println(map.get(2));  //coffee
        //获取键值对的数量
        System.out.println("键值对数量:" + map.size());  //键值对数量:4
        //通过key删除key-value
        map.remove(1);
        System.out.println("键值对数量:" + map.size()); //键值对数量:3
        //判断是否包含某个key
        System.out.println(map.containsKey(2));  //true
        //判断是否包含某个value
        System.out.println(map.containsValue("coffee")); //true
        //获取所有的value
        Collection<String> values = map.values();
        for (String value:
             values) {
            System.out.printf(value + " "); //coffee is nice
        }
        System.out.println();
        //清空map
        map.clear();
        System.out.println("键值对数量:" + map.size()); //键值对数量:0
        //判断集合是否为空
        System.out.println(map.isEmpty());//true
    }
}
  • Map集合遍历

    方法一:获取所有的key通过遍历key来遍历value。

public class MapTest {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"ice");
        map.put(2,"coffee");
        map.put(3,"is");
        map.put(4,"nice");
        //遍历map集合:获取所有的key。
        Set<Integer> keys = map.keySet();
        //遍历key,通过key获取value
        for (Integer key:
             keys) {
            System.out.printf(key + "=" + map.get(key) + " "); //1=ice 2=coffee 3=is 4=nice
        }
    }
}

​ 方法二:Set<Map.Entry<K,V>> entrySet()

​ 这个方法就是把Map集合直接全部转换成Set集合。Set集合的类型是Map.Entry

public class MapTest {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"ice");
        map.put(2,"coffee");
        map.put(3,"is");
        map.put(4,"nice");
        
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //遍历Set集合
        for (Map.Entry<Integer,String> s:
             set) {
            //System.out.printf(s.getKey() + "=" + s.getValue() + " ");
            System.out.printf(s + " "); //1=ice 2=coffee 3=is 4=nice 
        }
    }
}

2. HashMap

  • HashMap集合底层是哈希表/散列表的数据结构。不可重复,如果key重复了,value会直接覆盖。

    常用方法:

    map.put(k,v)

    v = map.get(k)

  • 如果所有的hashCode()方法返回值固定某个值,会导致底层哈希表变成了纯单项链表。

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

  • HashMap中key部分元素不可重复。

    public class HashMapTest {
        public static void main(String[] args) {
            //Integer是key,其hashCode和equals都重写了
            Map<Integer,String> map = new HashMap<>();
            map.put(1111,"ice");
            map.put(6666,"coffee");
            map.put(7777,"is");
            map.put(2222,"very");
            map.put(2222,"good");  //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.printf(entry.getKey() + "=" + entry.getValue() + "  ");  //7777=is  1111=ice  6666=coffee  2222=good
            }
        }
    }
    
  • HashMap集合的个数必须是2的倍数,能够提高HashMap的存储效率。

  • HashMap集合key部分的元素是放到了HashSet集合中,所以HashSet集合的的元素需要同时重写hashCode()和equals()方法

  • 如果一个方法的equals()方法重写了,必须重写hashCode(),如果equals()方法返回的是true,hashCode()返回值必须一样。

    注:一般hashCode和equals方法在IDEA同时生成

//向Map中存取,先调hashCode再调用equals
public class HashMapTest {
    public static void main(String[] args) {
        Student s1 = new Student("coffee");
        Student s2 = new Student("coffee");

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

        //重写equals方法之后
        System.out.println(s1.equals(s2));//true
		
        //没有重写hashCode()方法前,s1和s2的hashCode返回值不一样
        System.out.println("s1的hashCode:" + s1.hashCode());   //1163157884
        System.out.println("s2的hashCode:" + s2.hashCode());   //1956725890
        
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);

        //按理说,s1和s2一样,两个存入hashSet中,不可重复只会有一个。
        //这里如果没有重写hashCoe(),则存入为两个
        //这里重写hashCoe(),则存入只有一个
        System.out.println(students.size());
    }
}
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; }
    //重写equals方法
    public boolean equals(Object o) {
        if (o == null || !(o instanceof Student)) return false;
        if(o == this) return true;
        Student s = (Student) o;
        if(this.name == s.name) return true;
        return false;
    }
    //重写hashCode方法
    public int hashCode() {
        return Objects.hash(name);
    }
}
  • 放在HashMap集合key部分以及放在hashSet集合中元素,需要同时重写hashCode方法和equals方法。

  • HashMap允许key值或value值为null,但是只能有一个。

public class HashMapTest {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put(null,null);
        System.out.println(map.get(null));   //null
        map.put(null,100);
        System.out.println(map.get(null));  //100
    }
}

3. Hashtable

  • Hashtable的key和value都是不能为null的。
public class HashTableTest {
    public static void main(String[] args) {
        Map map = new Hashtable();
        map.put(100,null); //java.lang.NullPointerException
        map.put(null,"123");  //java.lang.NullPointerException
    }
}
  • Hashtable是线程安全的。效率较低,较少使用。

  • Properties是一个Map集合,继承Hashtable,Properties的key和value都是String。Properties被称为属性对象。

public class PropertiesTest {
    public static void main(String[] args) {
        //创建一个Properties对象
        Properties pro = new Properties();
        //Properties存方法
        pro.setProperty("url","jdbc:mysql://localhost:3306/ice");
        pro.setProperty("driver","com.mysql.jdbc.Driver");
        pro.setProperty("username","root");
        pro.setProperty("password","123");
        //Properties取方法
        System.out.println(pro.getProperty("url"));  //jdbc:mysql://localhost:3306/ice
        System.out.println(pro.getProperty("driver"));  //com.mysql.jdbc.Driver
        System.out.println(pro.getProperty("username"));  //root
        System.out.println(pro.getProperty("password"));  //123
    }
}

4. TreeSet

  • TreeSet底层实际上是一个TreeMap,TreeMap集合是一个二叉树。
  • 放到TreeSet集合中元素,等同于放到TreeMap集合key部分。
  • TreeSet中集合不可重复,但是可以按照元素大小自动排序。
public class TreeSetTest	 {
    public static void main(String[] args) {
        //创建一个TreeSet集合
        TreeSet<String> ts = new TreeSet<>();
        //添加String
        ts.add("ice");
        ts.add("coffee");
        ts.add("is");
        ts.add("very");
        ts.add("good");
        for (String t:
             ts) {
            System.out.printf(t + " ");  //coffee good ice is very
        }

        System.out.println();

        TreeSet<Integer> ts2 = new TreeSet<>();
        ts2.add(100);
        ts2.add(254);
        ts2.add(54);
        ts2.add(21);
        ts2.add(100);
        for (Integer t:
             ts2) {
            System.out.printf(t + " ");  //21 54 100 254
        }
    }
}
  • TreeSet无法对自定义类型进行排序
public class TreeSetTest {
    public static void main(String[] args) {
        Person p1 = new Person(15);
        TreeSet<Person> persons = new TreeSet<>();
        persons.add(p1);  //Exception in thread "main" java.lang.ClassCastException: collection.Person cannot be cast to java.lang.Comparable
    }
}
class Person{
    int age;
    public Person(int age){
        this.age = age;
    }
}
  • 自定义类型需要排序时,需要实现Comparable接口。Comparable接口中需要实现compareTo()方法。

    compareTo()方法:

    返回0表示相同,value会覆盖

    返回<0,会在左子树上查找

    返回>0,会在右子树上查找

public class TreeSetTest {
    public static void main(String[] args) {
        Cutomer c1 = new Cutomer(32);
        Cutomer c2 = new Cutomer(26);
        Cutomer c3 = new Cutomer(45);
        Cutomer c4 = new Cutomer(23);
        TreeSet<Cutomer> cs = new TreeSet<>();
        cs.add(c1);
        cs.add(c2);
        cs.add(c3);
        cs.add(c4);
        for (Cutomer c:
             cs) {
             System.out.printf(c.age + " ");  //23 26 32 45 
        }
    }
}
//放在TreeSet集合中,需要实现java.lang.Comparable接口
//并实现compareTo方法,equals可以不写
class Cutomer implements Comparable<Cutomer>{
    int age;
    public Cutomer(int age){
        this.age = age;
    }
    
    //编写比较逻辑,或者比较规则。
    @Override
    public int compareTo(Cutomer c) {
        return this.age - c.age;  //从小到大
        //return  c.age - this.age;  从大到小
    }
}
  • TreeSet集合中元素可排序的第二种方式:使用比较器方式。这种方式适用于写多个比较规则。
public class TreeSetTest03 {
    public static void main(String[] args) {
        //创建TreeSet集合时,需要使用这个比较器
        TreeSet<Cutomer> cus = new TreeSet<>(new CutomerComparator());//传一个比较器进去
        cus.add(new Cutomer(23));
        cus.add(new Cutomer(45));
        for (Cutomer c:
             cus) {
            System.out.printf(c + " ");  //Cutomer{age=23} Cutomer{age=45} 
        }
    }
}
class Cutomer{
    int age;
    public Cutomer(int age){ this.age = age; }
    @Override
    public String toString() { return "Cutomer{" + "age=" + age + '}'; }
}
//单独编写比较器,比较器实现java.util.Comparator接口
class CutomerComparator implements Comparator<Cutomer> {
    @Override
    public int compare(Cutomer o1, Cutomer o2) {
        //指定比较规则,按照年龄排序
        return o1.age - o2.age;
    }
}

5.Collections工具类

  • 集合接口:java.util.Collection

  • 方便集合操作,集合工具类:java.util.Collections

    集合工具类常用方法:

    将集合转换成线程安全:synchronizedList()

    集合排序:sort()

public class CollectionsTest01 {
    public static void main(String[] args) {
        //ArrayList集合不是线程安全的
        List<String> list = new ArrayList<>();
        //变成线程安全的
        Collections.synchronizedList(list);

        //排序
        list.add("abf");
        list.add("abc");
        list.add("abe");
        list.add("aba");
        Collections.sort(list);
        for (String s:
             list) {
            System.out.printf(s + " ");  //aba abc abe abf
        }
        System.out.println();
        //对于自定义类型的排序,需要实现Comparable接口
        List<Cutomer2> cus2 = new ArrayList<>();
        cus2.add(new Cutomer2(30));
        cus2.add(new Cutomer2(20));
        cus2.add(new Cutomer2(25));
        Collections.sort(cus2);
        for (Cutomer2 c:
                cus2) {
            System.out.printf(c.age + " ");  //20 25 30
        }
        System.out.println();
        //Set集合如果想要用sort方法进行排序,需要将Set集合转换成list集合
        Set<String> set = new HashSet<>();
        set.add("king");
        set.add("king2");
        set.add("king3");
        List<String> mylist = new ArrayList<>(set);
        Collections.sort(mylist);
        for (String s:
             mylist) {
            System.out.printf(s + " ");//king king2 king3 
        }

    }
}
class Cutomer2 implements Comparable<Cutomer2>{
    int age;
    public Cutomer2(int age){
        this.age = age;
    }

    //编写比较逻辑,或者比较规则。
    @Override
    public int compareTo(Cutomer2 c) {
        return this.age - c.age;  //从小到大
        //return  c.age - this.age;  从大到小
    }
}

——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频

;