集合

集合

认真、细致。

此内容在学习过程中不断修改,仅供自己复习使用。

一、简介

1. 继承关系

​ Iterable
​ |
Collection -------------- Map ------------ Iterator
​ | ------------------------------ | -------------------- |
​ List & Set & Queue — SortedMap — ListIterator

2. Collection接口

​ 在JAVA类库中,集合类的基本接口是Collection接口。这个接口有两个基本方法:

public interface Collection<E>{
    boolean add(E element);
    Iterator<E> iterator(); //用于返回一个实现了Iterator接口的对象,可以遍历集合,详见五
    ...
}

二、List

​ List为有序、允许重复、线程不安全的集合
​ 两种访问方式:迭代器、索引

1. ArrayList

​ 动态数组:通过数组的复制实现

2. LinkList

​ Java中的链表都是双向链表

3. Vector

​ Vector为线程安全的集合

​ Vector 类的所有方法都是同步的。可以由两个线程安全地访问一个 Vector 对象。但是 ,如果由一个线程访问Vector , 代码要在同步操作上耗费大量的时间。 这种情况还是很常见的 。而 ArrayList 方法不是同步的,因此, 建议在不需要同步使用ArrayList , 而不要使用 Vector。

三、Set

1. 简介

​ Set为无序、不允许重复、线程不安全的集合。

​ 使用了散列表相关概念。

  • 可以理解为建议版的HashMap
  • 底层核心private transient HashMap<E,Object> map;
  • 其中,E为set集合的key,Object为private static final Object PRESENT = new Object();

2. 何为无序?

  • 为什么说Set是无序的?

    • 无序指Set集合的添加顺序与存储顺序不一致。

    • Set底层采用Map集合的key值存储,通过计算hash()值来进行存储(如何存储见Map),故存储顺序与添加顺序没有半毛钱关系。

    • 部分源码:

    • private transient HashMap<E,Object> map; //Set的存储空间
      private static final Object PRESENT = new Object();  //作为Map的value的对象
      public boolean add(E e) {
          return map.put(e, PRESENT)==null;
      }
      

四、Map

​ Map的两个常用实现:HashMap(键值映射)、TreeMap(树映射、搜索树)

1. HashMap

  • HaspMap底层实现采用了哈希表。处理散列冲突的方法为“链地址法”。

  • 核心数组transient Node<K,V>[] table;,也称之为“位桶数组”

  • JDK8以后,当链表长度大于8时,链表就转为红黑树,这样提高了查找效率。

  • HashMap线程不安全、效率高,允许key或value为null

  • HashTbale线程安全、效率低、不允许key或value为null

  • /*HashMap类的部分代码*/
    // aka 16,默认数组大小,数组大小为2的整数幂
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
    
    //核心数组,数组大小为2的整数幂
    transient Node<K,V>[] table; 
    
    //Node部分代码
    static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Node<K,V> next;
    }
    
    //hash值得计算方法
    static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    

2. HashMap存储键值对过程

在这里插入图片描述

  • hashCode()方法是Object类中的方法

3. HashMap获取数据get()

  • 1)获得key的hashCode,通过hash()散列算法得到hash值,进而定位到数组位置。
  • 2)在链表上一个一个比较key对象,通过equals()方法将key对象和链表上结点的key对象进行比较。
  • 3)返回equals()为true的结点对象的value对象

hashCode()方法和equals()方法的联系:

​ JAVA规定,两个内容相同(equals()为true)的对象必须具有相同的hashCode。如果equals()为true而hashCode不同,那么在整个存储过程中就发生了悖论。

4. 扩容问题

  • HashMap的位桶数组,初始大小为16。实际使用时,显然大小是可变的。如果位桶数组中的元素达到(0.75*数组长度),就要重新调整数组大小。
  • 扩容很耗时间,其本质是定义新的数组,并且拷贝旧数组。

5. TreeMap

  • HashMap和TreeMap区别:TreeMap一般不用,在需要排序是才使用TreeMap
  • TreeMap底层实现是红黑二叉树。
  • 自定义对象使用TreeMap存储时,若需要排序,需要实现Compareable接口

五、迭代器和遍历

1. 简介

​ Iterator接口包含四个方法:

public interface Iterator<E>{
    E next();
    boolean hasNext();
    void remove();
    default void forEachRemaining(Consumer<? super E> action);
    //注:jdk8以后允许在接口中定义静态方法和默认方法
    /*
    	接口中的默认方法不要求实现,所有继承该接口的类也继承了该方法
    */
}

​ 使用Iterator遍历集合的伪代码

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext()){
    String element = iter.next();
    //do something with element
}

//foreach循环
for(String str:c){
    //do something with str
}

​ 编译器简单的将foreach循环翻译为带有迭代器的循环。for each循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个抽象方法即iterator() ,而Collection接口继承了Iterable接口,因此,标准库中任何集合都可以使用for each循环。

​ 还可以直接用iterator.forEachRemaining(element -> do something with element) 直接遍历

2. 迭代器遍历List和Set

//Set和List一样
public static void testList(){
    List<String> list = new ArrayList<>();
    list.add("aa");
    list.add("bb");
    list.add("cc");

    Iterator<String> iterator = list.iterator();
    while(iterator.hasNext()){
        String temp = iterator.next();
        System.out.println(temp);
    }
}

3. 迭代器遍历Map

方式一:通过map的实体对象

public static void testMap1(){
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "aa");
    map.put(2, "dd");
    map.put(3, "gg");

    Set<Map.Entry<Integer, String>> set = map.entrySet();
    Iterator<Map.Entry<Integer, String>> iterator = set.iterator();

    while(iterator.hasNext()){
        Map.Entry<Integer, String> entry = iterator.next();
        System.out.println(entry.getKey() + ":" + entry.getValue());
    }
}

方式二:通过map的key集合

public static void testMap2(){
     Map<Integer, String> map = new HashMap<>();
     map.put(1, "qq");
     map.put(2, "cc");
     map.put(3, "pp");

     Set<Integer> set = map.keySet();
     Iterator<Integer> iterator = set.iterator();

     while(iterator.hasNext()){
         Integer temp = iterator.next();
         System.out.println(temp + ":" + map.get(temp));
     }
 }

4. 增强for循环遍历

  • 编译器简单的将foreach循环翻译为带有迭代器的循环。for each循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个抽象方法即iterator() ,而Collection接口继承了Iterable接口,因此,标准库中任何集合都可以使用for each循环。

5. forEachRemaining遍历

  • 暂不阐述

六、 Collections工具类

类java.util.Collections提供了Set、List、Map进行排序、填充、查找元素的辅助方法

  • void sort(List) //对List容器内的元素升序排序(自定义的类使用:Comparable接口)

  • void shuffle(List) //对List容器内的元素随机排列(并非升序或降序),shuffle:调动、把…变换位置

  • void reverse(List) //对List容器内的元素逆序排序(并非降序,而是对原序列反转)

  • void fill(List, Object) //用一个特定的对象重写List容器
    行排序、填充、查找元素的辅助方法

  • void sort(List) //对List容器内的元素升序排序(自定义的类使用:Comparable接口)

  • void shuffle(List) //对List容器内的元素随机排列(并非升序或降序),shuffle:调动、把…变换位置

  • void reverse(List) //对List容器内的元素逆序排序(并非降序,而是对原序列反转)

  • void fill(List, Object) //用一个特定的对象重写List容器

  • int binarySearch(List, Object) //对于顺序的List容器,采用折半查找法查找对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值