JAVA笔记(16)---集合- 详解 Set集合( Map 体系集合常用方法;哈希表;二叉树数据结构;Map集合,如何自定义集合排序规则 )

Posted 猿头猿脑的王狗蛋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA笔记(16)---集合- 详解 Set集合( Map 体系集合常用方法;哈希表;二叉树数据结构;Map集合,如何自定义集合排序规则 )相关的知识,希望对你有一定的参考价值。

哈希表:


  • 博主说不明白,博主百度,博主陷入尴尬 ....
  • hash表的本质其实就是数组,hash表中通常存放的是键值对Entry;哈希表就是根据 key 值来通过哈希函数计算得到一个值,这个值就是下标值,用来确定这个Entry要存放在哈希表中哪个位置;

哈希碰撞:

  • hash碰撞指的是,两个不同的值(比如张三、李四的学号)经过hash计算后,得到的hash值相同,后来的李四要放到原来的张三的位置,但是数组的位置已经被张三占了,导致冲突

 

二叉树数据结构:


 

 

Set 集合:


1.Set:(Collection<----Set)

1)Set 体系下的所有集合存储元素特点:无序不可重复(放进去和取出来的顺序不同);

2)Set 集合元素无下标;

2.HashSet:(Collection<----Set<---HashSet)

其实就是 new 了一个 HashMap 集合。向 HashSet 中储存元素,其实是放到了 HashMap 集合中;

3.SortedSet:(Collection<----Set<---SortedSet)

增加了可以将集合元素按大小排序的特性;

4.TreeSet:(Collection<----Set<---TreeSet)

其实就是 new 了一个 TreeMap 集合。向 TreeSet 中储存元素,其实是放到了 TreeMap 集合中;

 

Map 集合:


Map:

1.Map 集合与 Collection 集合 没有任何关系;

2.Map 集合以 key-value 键值对的方式储存元素;

3.key 和 value 储存的都是对象的内存地址;

4.key 无序且不可重复;

 

Map常用方法:

1.V  put ( key , value ) //将键值对放到 HashMap 中(元素重复则不再载入)

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

3.void clear ( ) //清空Map集合

4.boolean containsKey (Object key) //判断Map 中是否包含某 key

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

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

7.V remove(0bject key) // 通过key 删除键值对

8.int size ( ) //获取Map 集合中键值对的个数

9.Set<K> keySet ( ) // 获取Map 集合所有的key (所有的键是一个set集合)

10.Set <Map. Entry <K,V> > entrySe t( ) 

//将Map集合的key和value都转换为String,并拼接到一起(键值之间还拼接了个“=”),放到Set集合中,最后返回此Set集合

  这个返回的 Set 集合中的每个元素类型都是 Map . Entry(Map中的内部类)

11.Collection<V> values ( ) // 获取Map 集合中所有的value ,装到 Collection 里,并返回此集合

12.Map . get (Object key ) //返回 key 所对应的 value

13.Map . Entry <key类型 , value类型 >  // 此为Map中的一个内部类,内部类中有 getKey ( ) , getValue ( )  等方法

 

Map 使用须知:

1.向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!equals方法有可能调用,也有可能不调用;

1)拿 put ( k , v ) 举例,什么时候 equals 不会调用 ?

k. hashCode ( ) 方法返回哈希值,哈希值经过哈希算法转换成数组下标;数组下标位置上如果是null.,,equals不需要执行;

2)get ( k ) 举例,什么时候equals不会调用?

k. hashCode ( ) 方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上如果是null. equals不需要执行。

2.注意:

如果一个类的equals方法重写了,那么 hashCode ( ) 方法必须重写。并且equals方法返回如果是true , hashCode ( ) 方法返回的值必须一样;

equals方法返回true表示两个对象相同,在同一个单向链表上比较;那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。

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

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

5.对于哈希表数据结构来说:

  如果o1和o2的hash值相同,一定是放到同一个单向链表上;

  当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”;

 

栗子老师:四种遍历Map集合的方法

import java.util.*;
public class practice {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap();
        map.put(1, "value1");
        map.put(2, "value2");
        map.put(3, "value3");

        //第一种:二次取值(效率低)
        System.out.println("---Map.keySet(),Map.get(key) 二次取值---");
        for (Integer key : map.keySet()) {
            System.out.println( key + "=" + map.get(key));
        }

        //第二种:
        System.out.println("\\n---Map.entrySet(),迭代器 遍历key,value---");
        Iterator map1it = map.entrySet().iterator();
        while (map1it.hasNext()) {
            Map.Entry<Integer, String> entry = (Map.Entry<Integer, String>) map1it.next();
            System.out.println( entry.getKey() + "=" + entry.getValue());
        }

        //第三种:推荐,尤其是容量大时
        System.out.println("\\n---Map.entrySet(),foreach 遍历key,value---");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println( entry.getKey() + "=" + entry.getValue());
        }

        //第四种:
        System.out.println("\\n---Map.values() 遍历所有的value,但不能遍历key---");
        for (String v : map.values()) {
            System.out.println(v);
        }
    }
}

运行结果:
------------------------------------
---Map.keySet(),Map.get(key) 二次取值---
1=value1
2=value2
3=value3

---Map.entrySet(),迭代器 遍历key,value---
1=value1
2=value2
3=value3

---Map.entrySet(),foreach 遍历key,value---
1=value1
2=value2
3=value3

---Map.values() 遍历所有的value,但不能遍历key---
value1
value2
value3

Process finished with exit code 0

 

HashMap:(Map<---HashMap)

1.底层采用了哈希表数据结构;

2.非线程安全;

3.在默认的情况下,HashMap的容量是16,调用构造函数来初始化容量,一般为 2 的 n 次幂;默认加载因子为 0.75;扩容后,容量为原来的2倍;

4.在 JDK 8 之后,如果哈希表单向链表中元素超过8个,数据结构就会由单向链表变为红黑树数据结构;当红黑树上的节点数量小于6时,红黑树又会变为单向链表数据结构;这种特性也是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率;

5.HashMap 的 key 和 value 允许为null;

 

Hashtable:(Map<---Hashtable)

1.底层采用了哈希表数据结构,初始化容量为11,扩容为 原容量的2倍+1;

2.所有方法被 synchronized 关键词修饰,所以是线程安全的,效率较低,由于现在有其他更好保证线程安全的方案,所以 Hashtable 使用较少;

3.Hashtable 的 key 和 value 不允许为null;

Properties:(Map<---Hashtabl<---Properties)

1.Properties 被成为属性类;key 和 value 只支持 String 类型;

2.栗子老师:

package com.bjpowernode.javase.day2;
import java.util.Properties;
public class practice {
    public static void main(String[] args) {
        Properties properties=new Properties();
        //添加元素
        properties.setProperty("账户","ZBC123");
        properties.setProperty("密码","123456");
        //查找元素
        System.out.println(properties.getProperty("账户"));
        System.out.println(properties.getProperty("密码"));
    }
}

运行结果;
--------------------------------
ZBC123
123456

Process finished with exit code 0

 

SortedMap:(Map<---SortedMap)

放在 SortedMap 集合中的元素 key 部分会自动按大小排序;

TreeMap:(Map<---SortedMap<---TreeMap)

1.底层采用二叉树数据结构;

2.按大小自动排序的规则如何修改?

1)自定义类实现 Comparable <> 接口,再重写 compareTo ( ) 方法;

import java.util.*;
public class practice {
    public static void main(String[] args) {
        TreeSet<People> peoples=new TreeSet<>();
        peoples.add(new People(10,"zhangsan"));
        peoples.add(new People(11,"lisi"));
        peoples.add(new People(10,"zhangsi"));
        for (People people:peoples) {
            System.out.println(people);
        }
    }
}
//规则:优先从小到大排序年龄;
//      年龄相同,按首字母先后比较 name
class People<E> implements Comparable<People>{
    private int old;
    private String name;
    public People() {
    }
    public People(int old, String name) {
        this.old = old;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "old=" + old +
                ", name=\'" + name + \'\\\'\' +
                \'}\';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        People people = (People) o;
        return old == people.old &&
                Objects.equals(name, people.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(old, name);
    }

    @Override
    public int compareTo(People o) {
        if (this.old==o.old){
            return this.name.compareTo(o.name);
        }
        return this.old-o.old;
    }
}

运行结果:
---------------------------------
Student{old=10, name=\'zhangsan\'}
Student{old=10, name=\'zhangsi\'}
Student{old=11, name=\'lisi\'}

Process finished with exit code 0
  

2)在创建 TreeSet 对象时,实现匿名内部类 Comparator,并重写 compare(object o1, object o2)方法

import java.util.*;
public class practice {
    public static void main(String[] args) {
        //规则:优先从小到大排序年龄;
        //年龄相同,按首字母先后比较 name
        TreeSet<People> peoples = new TreeSet<>(new Comparator<People>() {
            @Override
            public int compare(People o1, People o2) {
                if (o1.getOld() == o2.getOld()) {
                    return o1.getName().compareTo(o2.getName());
                }
                return o1.getOld() - o2.getOld();
            }
        });
        peoples.add(new People(10,"zhangsan"));
        peoples.add(new People(11,"lisi"));
        peoples.add(new People(10,"zhangsi"));
        for (People people:peoples) {
            System.out.println(people);
        }
    }
}
class People<E> {
    private int old;
    private String name;
    public People() {
    }
    public People(int old, String name) {
        this.old = old;
        this.name = name;
    }
    public int getOld() {
        return old;
    }
    public void setOld(int old) {
        this.old = old;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "old=" + old +
                ", name=\'" + name + \'\\\'\' +
                \'}\';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        People people = (People) o;
        return old == people.old &&
                Objects.equals(name, people.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(old, name);
    }
}

运行结果:
--------------------------
同上,不信可以去试试哦 ~ ~

3)自定义比较器对象,并实现 Comparator< > 接口,重写 compare 方法,然后在 TreeSet 的构造方法中 new 出比较器对象

package com.bjpowernode.javase.day2;
import java.util.*;
public class practice {
    public static void main(String[] args) {
        TreeSet<People> peoples = new TreeSet<>(new PeopleComparator());
        peoples.add(new People(10,"zhangsan"));
        peoples.add(new People(11,"lisi"));
        peoples.add(new People(10,"zhangsi"));
        for (People people:peoples) {
            System.out.println(people);
        }
    }
}
class People<E> {
    private int old;
    private String name;
    public People() {
    }
    public People(int old, String name) {
        this.old = old;
        this.name = name;
    }
    public int getOld() {
        return old;
    }
    public void setOld(int old) {
        this.old = old;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "old=" + old +
                ", name=\'" + name + \'\\\'\' +
                \'}\';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        People people = (People) o;
        return old == people.old &&
                Objects.equals(name, people.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(old, name);
    }
}
//规则:优先从小到大排序年龄;
//年龄相同,按首字母先后比较 name
class PeopleComparator implements Comparator<People>{
    @Override
    public int compare(People o1, People o2) {
        if (o1.getOld() == o2.getOld()) {
            return o1.getName().compareTo(o2.getName());
        }
        return o1.getOld() - o2.getOld();
    }
}

运行结果:
------------------------------
同上上

随笔:


在自定义类实现了 Comparable <> 接口,并重写了 compareTo ( ) 方法 的前提下,使用 Collection 工具类 ,也可以达到排序的目的:

1)Collections.sort(集合) ; 

public static void main(String[] args) {
        List<People> peoples=new ArrayList<>();
        peoples.add(new People(10,"zhangsan"));
        peoples.add(new People(11,"lisi"));
        peoples.add(new People(10,"zhangsi"));
        Collections.sort(peoples);
        for (People people:peoples) {
            System.out.println(people);
        }
    }

2)Collections.sort ( 集合 ,new 集合对应的比较器对象 );

public static void main(String[] args) {
        List<People> peoples=new ArrayList<>();
        Collections.sort(peoples,new PeopleComparator());
        peoples.add(new People(10,"zhangsan"));
        peoples.add(new People(11,"lisi"));
        peoples.add(new People(10,"zhangsi"));
        for (People people:peoples) {
            System.out.println(people);
        }
    }

由于博主目前只是一只猿宝宝,所以有些地方可能说的有些片面,若前辈们能够指点一二就更好了      (~ ̄(OO) ̄)ブ

 

以上是关于JAVA笔记(16)---集合- 详解 Set集合( Map 体系集合常用方法;哈希表;二叉树数据结构;Map集合,如何自定义集合排序规则 )的主要内容,如果未能解决你的问题,请参考以下文章

Python全栈笔记03 [模块二] 16-17 Oct Set 集合,三目运算

java集合Set集合之EnumSet详解

Java集合中List,Set以及Map等集合体系详解

java集合Set集合之LinkedHashSet 详解

java集合Set集合之HashSet详解

java集合Set集合之TreeSet详解