集合学习(一)

集合简单介绍

Map接口和Collection接口是所有集合框架的父接口。

Collection接口

java中没有提供Collection接口的实现,而是让List接口和set接口去继承它。

public interface List<E> extends Collection<E>

List接口的实现类有:ArrayList、LinkedList、Vector。

public interface Set<E> extends Collection<E>

set接口的实现类有:HashSet、TreeSet、LinkedHashSet。

Map接口

Map接口的实现类有:HashMap、LinkedHashMap、HashTable、TreeMap.

List:有序,可以重复的集合。

List 接口的三个典型实现:

①、List list1 = new ArrayList();

底层数据结构是数组,查询快,增删慢;线程不安全,效率高。初始大小为10,每次扩容为原来的0.5倍。

②、List list2 = new Vector();

底层数据结构是数组,查询快,增删慢;线程安全,效率低,几乎已经淘汰了这个集合。每次扩容为原来的1倍。

③、List list3 = new LinkedList();

底层数据结构是链表,查询慢,增删快;线程不安全,效率高

Set:无序,不可重复的集合。

1、Set hashSet = new HashSet();

①、HashSet:不能保证元素的顺序;不可重复;不是线程安全的;集合元素可以为 NULL;

②、其底层其实是一个数组,存在的意义是加快查询速度。我们知道在一般的数组中,元素在数组中的索引位置是随机的,元素的取值和元素的位置之间不存在确定的关系,因此,在数组中查找特定的值时,需要把查找值和一系列的元素进行比较,此时的查询效率依赖于查找过程中比较的次数。而 HashSet 集合底层数组的索引和值有一个确定的关系:index=hash(value),那么只需要调用这个公式,就能快速的找到元素或者索引。
③、对于 HashSet: 如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。
 1、当向HashSet集合中存入一个元素时,HashSet会先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置
   1.1、如果 hashCode 值不同,直接把该元素存储到 hashCode() 指定的位置
   1.2、如果 hashCode 值相同,那么会继续判断该元素和集合对象的 equals() 作比较
       1.2.1、hashCode 相同,equals 为 true,则视为同一个对象,不保存在 hashSet()中
       1.2.2、hashCode 相同,equals 为 false,则存储在之前对象同槽位的链表上,这非常麻烦,我们应该约束这种情况,即保证:如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。

2、Set linkedHashSet = new LinkedHashSet();
  不可以重复,有序。因为底层采用 链表 和 哈希表的算法。链表保证元素的添加顺序,哈希表保证元素的唯一性。

3、Set treeSet = new TreeSet();
  有序;不可重复,底层使用 红黑树算法,擅长于范围查询。需实现Comparable或compareTo接口。

以上三个 Set 接口的实现类比较:

共同点:
  1、都不允许元素重复
  2、都不是线程安全的类,解决办法:Set set = Collections.synchronizedSet(set 对象)
不同点:
HashSet:不保证元素的添加顺序,底层采用 哈希表算法,查询效率高。判断两个元素是否相等,equals() 方法返回 true,hashCode() 值相等。即要求存入 HashSet 中的元素要覆盖 equals() 方法和 hashCode()方法

LinkedHashSet:HashSet 的子类,底层采用了 哈希表算法以及 链表算法,既保证了元素的添加顺序,也保证了查询效率。但是整体性能要低于 HashSet

TreeSet:不保证元素的添加顺序,但是会对集合中的元素进行排序。底层采用 红-黑 树算法(树结构比较适合范围查询)

注意:尽量使用迭代器对非线程安全的集合进行遍历。
原因:在使用迭代器的过程中有其他线程修改了集合,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对集合内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 集合。

使用场景:

在这里插入图片描述
(此图来源于https://blog.csdn.net/feiyanaffection/article/details/81394745)

Map:key-value 的键值对,key 不允许重复,value 可以

严格来说 Map 并不是一个集合,而是两个集合之间 的映射关系。
这两个集合没每一条数据通过映射关系,我们可以看成是一条数据。即 Entry(key,value)。Map 可以看成是由多个 Entry 组成。
因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历。
遍历实例:

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

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("first", "linlin");
        map.put("second", "好好学java");
        map.put("third", "sihai");
        map.put("first", "sihai2");


        // 第一种:通过Map.keySet遍历key和value
        System.out.println("===================通过Map.keySet遍历key和value:===================");
        for (String key : map.keySet()) {
            System.out.println("key= " + key + "  and  value= " + map.get(key));
        }

        // 第二种:通过Map.entrySet使用iterator遍历key和value
        System.out.println("===================通过Map.entrySet使用iterator遍历key和value:===================");
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key= " + entry.getKey() + "  and  value= "
                    + entry.getValue());
        }

        // 第三种:通过Map.entrySet遍历key和value
        System.out.println("===================通过Map.entrySet遍历key和value:===================");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + "  and  value= "
                    + entry.getValue());
        }

        // 第四种:通过Map.values()遍历所有的value,但是不能遍历键key
        System.out.println("===================通过Map.values()遍历所有的value:===================");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
    }

}

1、HashMap hashMap = new HashMap();

key的值可以为null,但只有一个key为null;它是线程不安全的。

底层实现

HashMap以键值对(key-value)的形式来存储元素,在调用Put方法时,HashMap会通过hash函数来计算key的hash值,然后通过hash值来判断存储位置是否已存在元素,如果存在的话,就判断要存储的元素与当前位置元素的key是否相同,如果相同则覆盖,如果不同的话就会发送哈希冲突,将冲突的值加到链表中即可。Jdk1.8后,当链表的长度大于8时,链表转为红黑树。

红黑树

  • 节点是红色或者黑色
  • 根节点都是黑色
  • 每个叶子的节点都是黑色的空节点
  • 每个红色节点的两个子节点都是黑色的
  • 从任意节点到其每个叶子的所有路径都包含相同的黑色节点
    在这里插入图片描述
    (图片来源https://www.cnblogs.com/linliquan/p/11323172.html)

2、LinkedHashMap linkedHashMap= new LinkedHashMap();

有序的,使用链表来扩展HashMap。

3、HashTable hashTable= new HashTable();

相比HashMap,它是线程安全的。除此之外,key和value的值都不能为null。

4、TreeMap treeMap= new TreeMap();

基于红黑树数据结构实现,键值使用compareTo或comparable接口来排序。

此文章借鉴的博客有:

https://www.cnblogs.com/ysocean/p/6555373.html

https://blog.csdn.net/feiyanaffection/article/details/81394745

https://www.cnblogs.com/linliquan/p/11323172.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值