1

Java 集合Collection和Map详解

一:集合由来
我们要求能在任何时候,任何地点创建任意数量的对象,而这些对象用什么来容纳呢?,我们首先想到了数组,但是数组只能存放同一类型的数据,而且长度是固定的,故集合变应运而生
Java集合类存放在java.util包中,是一个用来存放对象的容器。
1.集合只能存放对象。比如你存入一个int型数据66放入集合中,其实它是自动转换成Integer类后存入的,Java中每一种基本数据类型都有对应的引用类型。
2.集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。对象本身还是放在堆内存中。
3.集合可以存放不同类型,不限数量的数据类型。
二:Java集合框架
image.png
集合和数组的区别
image.png
三:Collection集合和Iterator迭代器
我们先来看一看List集合和Set集合的父类Collection集合,以及实现的迭代器的接口
查看Iterator源码主要有三个方法:

public interface Iterator<E> {
//判断容器内是否还有可供访问的元素
 boolean hasNext();
 //返回迭代器刚越过的元素的引用,返回值是对象Object需要强制转换成自己需要的类型。(泛型)
  E next();
  //删除迭代器刚越过的元素。
  default void remove() {}
        
}

一般迭代器Iterator 使用:

  List<String> list=new ArrayList<>();
        //添加元素
        list.add("Rocky");
        list.add("Tom");
        list.add("Mark");
        //list.iterator()构造List的迭代器
        Iterator iterator=list.iterator();
        //通过迭代器遍历元素
        while (iterator.hasNext()){
           Object obj= iterator.next();
           System.out.println(obj);
        }
        
        
        //方式二,增加一个删除
         List<String> list=new ArrayList<>();
        //添加元素
        list.add("Rocky");
        list.add("Tom");
        list.add("Mark");
        //list.iterator()构造List的迭代器
        Iterator<String> iterator=list.iterator();//Iterator集合具体实例的String对象
        //通过迭代器遍历元素
        while (iterator.hasNext()){
           String str= iterator.next();
           if ("Rocky".equals( str)){
               //删除元素
               iterator.remove();
           }else {
               System.out.println(str);
           }

        }

Collection接口规定了集合的一些基本操作
查看Collection源码解析:

//Collection接口继承了迭代器的接口
public interface Collection<E> extends java.lang.Iterable<E> {
//返回集合的数量
public int size();
//判断集合是否为空
public boolean isEmpty();
//判断集合是否包含某些元素
public boolean contains(Object o);
//判断集合是否包含某些集合
public boolean containsAll(Collection c);
//在集合末尾添加元素
public boolean add(E e);
//删除该元素,并返回true
public boolean remove(Object o);
//清空集合
public void clear();
//迭代器集合专用遍历方式
public Iterator iterator();
//返回一个包含了本类集中所有元素的数组,数组类型为Object[]
public  Object[] toArray();

public boolean equals(Object o);
public int hashCode();
}

三:集合具体实现List
Collection 接口的接口 对象的集合(单列集合)

List 接口:元素按进入先后有序保存,可重复 
      LinkedList 接口实现类, 链表, 插入删除,增删快 没有同步, 线程不安全 
      ArrayList 接口实现类, 数组, 随机访问, 增删慢 没有同步, 线程不安全 
      Vector 接口实现类 数组, 同步, 增删慢 线程安全 
            Stack 是Vector类的实现类 
Set 接口: 仅接收一次,不可重复,并做内部排序 
      HashSet 使用hash表(数组)存储元素 
      LinkedHashSet 链表维护元素的插入次序 
      TreeSet 底层实现为二叉树,元素排好序

image.png
1.ArrayList
ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
ArrayList是基于数组实现的List类,它封装了一个动态的增长的、允许再分配的Object[]数组
查看源码:
除了继承Collection接口的方法外

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//返回指定元素在集合的第一次出现的索引
 public int indexOf(Object o) {}
 //返回指定元素在集合的最后一次出现的索引
 public int lastIndexOf(Object o) {}
 //获取指定位置的元素
 public E get(int index) {}
  //修改指定索引位置的元素值,修改为指定值,返回修改前的值
 public E set(int index, E element) {}
 //列表迭代器
 public ListIterator<E> listIterator(){}
}
//截取集合是[);如subList(1,3)只包含1,2前闭后开
public List<E> subList(int fromIndex, int toIndex) {}
//移除索引对应的元素
public E remove(int index);

ArrayList底层实现是数组,是有序的,可重复的,查询快(数组的特点),增删慢
2.LinkedList
LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
implements List<E>, Deque<E>。实现List接口,能对它进行队列操作,即可以根据索引来随机访问集合中的元素。同时它还实现Deque接口,即能将LinkedList当作双端队列
LinkedList源码解析

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;//大小
 transient Node<E> first;//头节点
  transient Node<E> last;//尾节点
  //添加头元素
public void addFirst(E e)
//添加尾元素
public void addLast(E e)
}
//获取头元素
 public E getFirst() 
 //获取尾元素
  public E getLast()
  //移除头元素
  public E removeFirst()
  //移除尾元素
   public E removeLast()
   //入栈,其实就是在头部添加元素
   public void push(E e)
   //安全的添加操作,在尾部添加
    public boolean offer(E e)
    //在头部添加
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }
    //尾部添加
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }
    //删除头部
    public E pop() {
        return removeFirst();
    }
    public E pollFirst() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

public E pollLast() {
    final Node<E> l = last;
    return (l == null) ? null : unlinkLast(l);
}
**LinkedList集合中List接口的实现类。存取有序,有索引,可重复,查询慢,增删快(链表的特点)**
   

Vetor
一般使用

//方式一通过Enumeration
//这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。
 Vector vector=new Vector();
        //添加元素
        vector.add("Rocky");
        vector.add("Tom");
        vector.add("Mark");
        Enumeration<String> enumeration=vector.elements();
    
        while (enumeration.hasMoreElements()){
            String str= enumeration.nextElement();

            System.out.println(str);
            }
            
    //方式二,通过迭代器
    Vector vector=new Vector();
        //添加元素
        vector.add("Rocky");
        vector.add("Tom");
        vector.add("Mark");
Iterator<String> iterator=vector.iterator();
  //通过迭代器遍历元素
     while (iterator.hasNext()){
           String str= iterator.next();
           if ("Rocky".equals( str)){
               //删除元素
               iterator.remove();
           }else {
               System.out.println(str);
           }

        }

Enumeration接口源码:

//可以看到Enumeration没有删除方法
public interface Enumeration<E> {
boolean hasMoreElements();//类似于Iterator的hasNext()
 E nextElement();//类似于迭代器iterator的next();
}

Vector与ArrayList类似, 内部同样维护一个数组, Vector是线程安全的. 方法与ArrayList大体一致, 只是加上 synchronized 关键字, 保证线程安全
Vector的源码解析:

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//主要字段 存储的数组
protected Object[] elementData;
//主要字段 存储元素的个数
 protected int elementCount;
 //扩容时的增加量,大于0增加capacityIncrement否则翻倍
  protected int capacityIncrement;
  
   public Vector() {
        this(10);//默认是10
    }
    //初始化时候容量为10
    //容量大于0增加为capacityIncrement,否则翻倍
     public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
}
//根据索引获取元素synchronized 用了这个关键词修饰,同步
 public synchronized E elementAt(int index) {}
 //添加集合 
 public synchronized boolean addAll(Collection<? extends E> c){}
 //从index所表示的下标处开始搜索obj.
 public synchronized int indexOf(Object o, int index) {}
  //从向量尾部开始逆向搜索obj.
 public synchronized int lastIndexOf(Object o){}
   //删除index所指的地方的对象
 public fianl synchronized void removeElementAt(int index) 
 //将index处的对象设置成obj,原来的对象将被覆盖。
 public final synchronized void setElementAt(Object obj,int index) 
 //获取向量对象中的首个obj
public final synchornized firstElement() 

Vector是有序的,可以重复的。

四:集合的具体实现Set类
1.HashSet
Hashset底层是hashMap维护数据的表
因为数组的索引是连续的而且数组的长度是固定的,无法自由增加数组的长度。而HashSet就不一样了,HashCode表用每个元素的hashCode值来计算其存储位置,从而可以自由增加HashCode的长度,并根据元素的hashCode值来访问元素。而不用一个个遍历索引去访问,这就是它比数组快的原因。

一般使用

//创建集合
 Set<String> name=new HashSet<>();
 //添加元素
        name.add("Rocky");
        name.add("Jack");
        name.add("Tom");
         //name.add("Jack");
        //添加相同的元素输出结果没变
        //构建迭代器
        Iterator<String> iterator1=name.iterator();
        while (iterator1.hasNext()){
            String s=iterator1.next();
            System.out.println(s);
        }
    }
    
    //结果
System.out: Rocky
System.out: Tom
System.out: Jack

Hashset的集合详解

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
 private transient HashMap<E,Object> map;
 //HashSet这个类实现了Set集合,实际为一个HashMap的实例
 //无参数的构造函数,此构造函数创建一个大小为16的容器,加载因子为0.75
   public HashSet() {
        map = new HashMap<>();
    }
    //是否包含对象
      public boolean contains(Object o) {
        return map.containsKey(o);
    }
    //添加元素
     public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    //删除元素
    public boolean remove(Object o)
    //从此 set 中移除所有元素。
     public void clear() {
        map.clear();
    }
}

HashSet:无序,不能重复
2.LinkedHashSet
HashSet还有一个子类LinkedHashSet,LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的,也就是说当遍历集合LinkedHashSet集合里的元素时,集合将会按元素的添加顺序来访问集合里的元素。
输出集合里的元素时,元素顺序总是与添加顺序一致。但是LinkedHashSet依然是HashSet,因此它不允许集合重复
上面说的的就是输出结果是和添加顺序是一致的,但是set一样不能有重复的元素(有序,不能重复
LinkedHashSet源码解析:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    //构造方法,默认是16容量,加载因子0.75
 public LinkedHashSet() {
        super(16, .75f, true);
    }
    应为是Hashset子类,就不多余赘述了,就是有序
}

3.TreeSet
TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
TreeSet内部实现的是红黑树,默认整形排序为从小到大。
一般使用


        TreeSet<Integer> name=new TreeSet<>();
        name.add(12);
        name.add(11);
        name.add(16);
        name.add(15);
        name.add(9);
        Iterator<Integer> iterator1=name.iterator();
        while (iterator1.hasNext()){
            Integer s=iterator1.next();
            System.out.println(s);
        }
        //返回在这个集合中小于或者等于给定元素的最大元素(<= 给定的最大元素)
        System.out.println("Floor value for 12: "+name.floor(12));
        //返回在这个集合中大于或者等于给定元素的最小元素(>=给定元素的最小元素)
        System.out.println("Ceiling value for 12: "+name.ceiling(12));
        //返回集合中的开始一个元素
        System.out.println("First value : "+name.first());
        //返回集合中的最后一个元素
        System.out.println("Last value: "+name.last());
        //返回指定元素12之后的元素。
        System.out.println("High value for 13: "+name.higher(12));
        //返回指定元素12之前的元素。
        System.out.println("Low value for 13: "+name.lower(12));
    
    //结果
System.out: 9
System.out: 11
System.out: 12
System.out: 15
System.out: 16
System.out: Floor value for 12: 12
System.out: Ceiling value for 12: 12
System.out: First value : 9
System.out: Last value: 16
System.out: High value for 12: 15
System.out: Low value for 12: 11

//上面输出的数据已经是有顺序的了,什么原因了
//TreeSet会调用集合元素的compareTo(Objec obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这就是自然排序

源码解析

//实现的NavigableSet,public interface NavigableSet<E> extends SortedSet<E> 继承与于SortedSet
//SortSet接口,主要提供了一些排序方法
// Comparator<? super E> comparator();
// SortedSet<E> subSet(E fromElement, E toElement);返回此Set的子集合,含头不含尾
 //SortedSet<E> headSet(E toElement);返回此Set的子集,由小于toElement的元素组成;
 //SortedSet<E> tailSet(E fromElement);返回此Set的子集,由大于fromElement的元素组成;
 //E last();返回集合中的最后一个元素;
 //E first();返回集合中的第一个元素;
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
//包含什么元素
 public boolean contains(Object o) {
        return m.containsKey(o);
    }
    //添加元素
     public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
    //移除元素
     public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }
    //还有就是SortSet接口的实现方法
     public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                                  E toElement,   boolean toInclusive) 
      // 返回集合中的最后一个元素                           
      public E last() {
        return m.lastKey();
    }
    //返回指定元素之前的元素。
     public E lower(E e) {
        return m.lowerKey(e);
    }
    //返回在这个集合中小于或者等于给定元素的最大元素(<= 给定的最大元素)
    public E floor(E e) {
        return m.floorKey(e);
    }
    //返回在这个集合中大于或者等于给定元素的最小元素(>=给定元素的最小元素)
     public E ceiling(E e) {
        return m.ceilingKey(e);
    }
    //返回指定元素之后的元素。
     public E higher(E e) {
        return m.higherKey(e);
    }

}

五:Map集合
Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
TreeMap:非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
一般使用:

//创建Map对象
        //数据采用Hash表实现
        HashMap<String, String> map = new HashMap<>();
        //给map添加元素
        map.put("num:One","Java");
         //map.put("num:One","Java");即使我添加一个重复元素,结果一样
        map.put("num:Two","C");
        map.put("num:Three","Python");
        map.put("num:Four","C++");
        System.out.println(map);
        //结果
       System.out: {num:One=Java, num:Four=C++, num:Two=C, num:Three=Python} 
       //存取顺序不一致,不重复

分析Map的源码,解析Map的方法

public interface Map<K, V> {
//返回该Map里面的键值对的个数
public int size();
//查询map是否为空,如果空则返回true
public boolean isEmpty();
//查询Map中是否包含指定的key,如果包含则返回true
public boolean containsKey(Object obj);
//查询Map中是否包含指定的Value,如果包含则返回true
public boolean containsValue(Object obj);
//返回指定key所对应的value,如果map中不包含key则返回null
public V get(Object key)
//添加一个键值对,如果已经有了相同的key值,则新的键值对覆盖旧的键值对
public V put(Object key,Object value)
//删除指定key所对应的键值对,返回可以关联的value,如果key不存在,返回null
public V remove(Object key)
//将指定Map中键值对复制到Map中
public void putAll(Map m)
//清除所有的键值对
public void clear();
//拿到所有的key的Set集合
Set<K> keySet();
//返回该Map里所有的Value组成的Collection
Collection values();
//这个拿到了键值对的 Entry<K, V>集合
Set<Map.Entry<K, V>> entrySet();
//内部类接口
public static interface Entry<K, V> {
//返回该Entry里包含的key值
public K getKey();
/返回该Entry里包含的value值
public V getValue();
/设置该Entry里包含的value值,并返回新的value值
public V setValue(V value);
}
}

使用

创建Map对象
        //数据采用Hash表实现
        HashMap<String, String> map = new HashMap<>();
        //给map添加元素
        map.put("num:One","Java");
        map.put("num:One","Java");
        map.put("num:Two","C");
        map.put("num:Three","Python");
        map.put("num:Four","C++");
        System.out.println(map);

        //第一种:普遍使用,二次取值
        System.out.println("通过Map.keySet遍历key和value:");
        for (String key : map.keySet()) {
            System.out.println("key= "+ key + " and value= " + map.get(key));
        }

        //第二种,通过内部类Entry
        System.out.println("通过Map.entrySet使用iterator遍历key和value:");
        //map.entrySet()拿到Set集合Entry<String, String>
        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()拿到Set(Entry<String, String> )集合,元素是Entry<String, String> 遍历并打印出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());
        }

        //第四种通过values拿到所有的value集合遍历所有的value值打印
        System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
        
        //结果
System.out: {num:One=Java, num:Four=C++, num:Two=C, num:Three=Python}

System.out: 通过Map.keySet遍历key和value:
System.out: key= num:One and value= Java
System.out: key= num:Four and value= C++
System.out: key= num:Two and value= C
System.out: key= num:Three and value= Python

System.out: 通过Map.entrySet使用iterator遍历key和value:
System.out: key= num:One and value= Java
System.out: key= num:Four and value= C++
System.out: key= num:Two and value= C
System.out: key= num:Three and value= Python

System.out: 通过Map.entrySet遍历key和value
System.out: key= num:One and value= Java
System.out: key= num:Four and value= C++
System.out: key= num:Two and value= C
System.out: key= num:Three and value= Python

System.out: 通过Map.values()遍历所有的value,但不能遍历key
System.out: value= Java
System.out: value= C++
System.out: value= C
System.out: value= Python

1.HashMap
基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。
LinkedHashMap是HashMap的唯一子类

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
//默认对map输入和输出是有序的
//HashMap和双向链表合二为一即是LinkedHashMap。所谓LinkedHashMap,其落脚点在HashMap
//因为有双向循环列表,所以LinkedHashMap能够记录插入元素的顺序
}

HashMap和HashTable的比较:
image.png
2.TreeMap
非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
image.png
TreeMap源码分析

//TreeMap这里实现的NavigableMap,public interface NavigableMap<K, V> extends java.util.SortedMap<K,V> 
//SortedMap是一个排序类和SortedSet类似
// SortedMap<K,V> subMap(K fromKey, K toKey);
// SortedMap<K,V> headMap(K toKey);
// SortedMap<K,V> tailMap(K fromKey);
// K firstKey();//返回最小key,如果为空,则返回null
// K lastKey();//返回最大key,如果为空,则返回null
// Set<K> keySet();
//  Collection<V> values();
//Set<Map.Entry<K, V>> entrySet();
public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
//适用于按自然顺序或自定义顺序遍历键(key)。
//包含key值,是返回true
  public boolean containsKey(Object key)
  ......
  
  //返回具有最小键值(大于或等于指定键)的键值对,如果没有这样的键,则返回null。(>=指定键的最小键的集合)
      public Map.Entry<K,V> ceilingEntry(K key) {}
        public K ceilingKey(K key){}
        //返回与小于或等于给定键元素(key_ele)的最大键元素链接的键值对(如果存在)。(<=指定键的最大键值的键值对)
      public Map.Entry<K,V> floorEntry(K key) {}
         public K floorKey(K key) {}
         //返回与严格大于给定键的最小键关联的键-值映射,如果没有这样的键,则返回null。
        public Map.Entry<K,V> higherEntry(K key) {}
        public K higherKey(K key){}
        //返回严格小于给定键(作为参数传递)的最大键
        public Map.Entry<K,V> lowerEntry(K key) {}
         public K lowerKey(K key){}
}

上面是集合的全部内容
集合Collection和Map用到了数据结构有数组,链表,Hash表,红黑树等

结尾:深入学习才是有效学习


Rocky_ruan
57 声望5 粉丝

不积跬步,无以至千里