Java 集合

本贴最后更新于 835 天前,其中的信息可能已经沧海桑田

集合作用

集合:对象的容器,定义了对多个对象进行操作的常用方法

集合按照其存储结构可以分为两大类:单列集合 java.util.Collection、双列集合 java.util.Map

集合与数组的区别:

长度 存储对象 存储数据类型
数组 固定 基本数据类型、同类型的对象 固定
集合 可变 不同类型的对象 不定

Collection

Collection 是所有单列集合的父接口,在 Collection 中定义了单列集合(List 和 Set)通用的方法,这些方法可用于操作所有的单列集合

数据类型 方法 说明
boolean add(E e) 指定的元素添加到集合中
void clear() 清空所有元素
boolean remove(Object o) 移除指定元素
boolean contains(Object o) 判断是否包含指定的元素
boolean isEmpty() 判断集合是否为空
int size() 返回集合的元素数
Object[] toArray() 集合中的元素存入数组
public class CollectionTest {
    public static void main(String[] args) {
        // 创建集合对象,可使用多态
        Collection<String> cell = new ArrayList<>();
        System.out.println(cell);  //重写了toString方法

        // 添加元素 add(E e)
        boolean b1 = cell.add("Hefery");
        System.out.println(b1);   //true,无需接收返回值
        System.out.println(cell); //[Hefery]

        cell.add("Hello");
        cell.add("World");

        // 移除元素 remove(Object o)
        boolean b2 = cell.remove("World");
        boolean b3 = cell.remove("abc");
        System.out.println(b2);  //true
        System.out.println(b3);  //false

        // 查询元素 contains(Object o)
        boolean b4 = cell.contains("Hefery");
        boolean b5 = cell.contains("Woorld");
        System.out.println(b4);  //true
        System.out.println(b5);  //false

        // 判空 isEmpty()
        boolean b6 = cell.isEmpty();
        System.out.println(b6);  //false

        // 元素个数 size()
        int b7 = cell.size();
        System.out.println(b7);  //2

        // 集合元素存入数组 toArray()
        Object[] arr = cell.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        // 清空 clear()
        cell.clear();
        System.out.println(cell);  //[]
    }
}

List

List 特点:

  1. 有序的 collection(也称为序列)
  2. 带索引,有下标
  3. 允许重复的元素

List 的常用子类:

  • ArrayList 底层结构是数组,底层查询快,增删慢
  • LinkedList 底层结构是链表,增删快,查询慢
  • Voctor 底层结构是数组,线程安全,增删慢,查询慢

ArrayList

public class ArrayList<E> extends AbstractList<E> implements List<E>

底层:数组
特点:查询快,增删慢,线程不安全
必须开辟连续空间

重要方法
数据类型 方法 说明
boolean add(E e) 将指定的元素添加到此列表的尾部
void add(int index, E element) 将指定的元素插入此列表中的指定位置
E remove(int index) 移除此列表中指定位置上的元素
E set(int index, E element) 用指定的元素替代此列表中指定位置上的元素
void clear() 移除此列表中的所有元素
Object[] toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组
E get(int index) 返回此列表中指定位置上的元素
boolean isEmpty() 如果此列表中没有元素,则返回 true
int size() 返回此列表中的元素数
boolean contains(Object o) 如果此列表中包含指定的元素,则返回 true
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class listDemo {
    public static void main(String[] args) {
        //创建 List 对象(多态)
        List<String> list = new ArrayList<>();

        //添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("a");
        System.out.println(list);

        //添加元素,在指定位置
        list.add(3,"d");
        System.out.println(list);

        //移除元素(位置)
        list.remove(3);
        System.out.println(list);

        //替换元素值
        list.set(3,"A");
        System.out.println(list);

        //遍历
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }

        for (String e:list) {
            System.out.println(e);
        }

        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String s0 = it.next();
            System.out.println(s0);
        }
    }
}
源码分析
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8683452581122892189L;

    /** 默认容量 */
    private static final int DEFAULT_CAPACITY = 10;

    /** 数组元素 */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /** 默认空数组 */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /** 存放元素 */
    transient Object[] elementData; // non-private to simplify nested class access

    /** 元素个数 */
    private int size;

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
        }
    }
  
    /** 无参构造 */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    /** 数组扩容:如果没有向集合中添加任何元素时,容量0,添加一个元素之后容量10,默认长度是10,当超过长度时,按50%延长集合长度 */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  
}  

LinkedList

底层:双向链表
特点:查询慢,增删快,线程不安全
无需开辟连续空间

重要方法
数据类型 方法 说明
boolean add(E e) 将指定的元素添加到此列表的尾部
void add(int index, E element) 将指定的元素插入此列表中的指定位置
void addFirst(E e) 将指定元素插入此列表的开头
void addLast(E e) 将指定元素添加到此列表的结尾
E remove(int index) 移除此列表中指定位置上的元素
E removeFirst() 移除并返回此列表的第一个元素
E removeLast() 移除并返回此列表的最后一个元素
E set(int index, E element) 用指定的元素替代此列表中指定位置上的元素
void clear() 移除此列表中的所有元素
Object[] toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组
E get(int index) 返回此列表中指定位置上的元素
E getFirst() 返回此列表的第一个元素
E getLast() 返回此列表的最后一个元素
boolean isEmpty() 如果此列表中没有元素,则返回 true
int size() 返回此列表中的元素数
boolean contains(Object o) 如果此列表中包含指定的元素,则返回 true
E pop() 从此列表所表示的堆栈处弹出一个元素,移除第一个元素
void push(E e) 将元素推入此列表所表示的堆栈,移除最后一个元素
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class listDemo {
    public static void main(String[] args) {
        demo01();
    }

    private static void demo01() {
        //创建 LinkedList 对象(不能使用多态)
        LinkedList<String> linkedlist = new LinkedList<>();

        //添加
        linkedlist.add("a");
        linkedlist.add("b");
        linkedlist.add("c");

        linkedlist.addFirst("www.");
        linkedlist.addLast(".cn");

        linkedlist.add(1,"hefery");

        linkedlist.push("lalala");

        System.out.println(linkedlist);  //[lalala, www., hefery, a, b, c, .cn]

        //获取
        System.out.println(linkedlist.getFirst()); //lalala
        System.out.println(linkedlist.getLast());  //.cn
        System.out.println(linkedlist.get(2));     //hefery

        //移除
        linkedlist.removeFirst();
        linkedlist.removeLast();
        linkedlist.pop();
        System.out.println(linkedlist);  //[hefery, a, b, c]

    }
}
源码分析
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 LinkedList() {
    }
  
    /** 节点结构 */
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
  
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }
  
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

}

Voctor

Vector 类可以实现可增长的对象数组,线程安全
底层:数组
特点:查询快,增删慢,线程安全

重要方法
数据类型 方法 说明
boolean add(E e) 将指定的元素添加到此列表的尾部
void add(int index, E element) 将指定的元素插入此列表中的指定位置
void addElement(E obj) 将指定的组件添加到此向量的末尾,将其大小增加 1
E remove(int index) 移除此列表中指定位置上的元素
E removeElement() 从此向量中移除变量的第一个(索引最小的)匹配项
E removeLast() 移除并返回此列表的最后一个元素
E set(int index, E element) 用指定的元素替代此列表中指定位置上的元素
void clear() 移除此列表中的所有元素
Object[] toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组
E get(int index) 返回此列表中指定位置上的元素
boolean isEmpty() 如果此列表中没有元素,则返回 true
int size() 返回此列表中的元素数
boolean contains(Object o) 如果此列表中包含指定的元素,则返回 true
Enumeration<E> elements() 返回此向量的组件的枚举,遍历

Set

HashSet

底层:哈希表,基于 HashMap 实现的
特点:不包含重复元素,无索引,无序

重要方法
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class setDemo {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();

        //添加
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(1);

        //遍历
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            Integer n = it.next();
            System.out.println(n);
        }

        for (Integer i : set) {
            System.out.println(i);
        }
    }
}
//存储自定义类型元素,需重写 hashCode() 和 equals() 
import java.util.HashSet;
/*
    重写 hashCode() 和 equals() 保证同名同龄的人只能存储一次
 */
public class hashCodeDemo {
    public static void main(String[] args) {
        HashSet<Person> set = new HashSet<>();

        Person p1 = new Person("Hefery",22);
        Person p2 = new Person("Hefery",22);
        Person p3 = new Person("Hefery",18);
        System.out.println(p1.hashCode());  //未重写:356573597     重写后:-1830346093
        System.out.println(p2.hashCode());  //未重写:1735600054    重写后:-1830346093
        System.out.println(p1==p2);         //未重写:false         重写后:false     比较地址
        System.out.println(p1.equals(p2));  //未重写:false         重写后:true

        set.add(p1);
        set.add(p2);
        set.add(p3);

        System.out.println(set);
        //未重写:[Person{name='Hefery', age=22}, Person{name='Hefery', age=22}, Person{name='Hefery', age=18}]
        //重写后:[Person{name='Hefery', age=22}, Person{name='Hefery', age=18}]
    }
}
源码分析
LinkedHashSet

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现
底层:哈希表(链表 + 红黑树) + 链表(记录元素存储顺序)

import java.util.HashSet;
import java.util.LinkedHashSet;

public class linkedhashsetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();

        set.add("www");
        set.add("abc");
        set.add("abc");
        set.add("cn");

        System.out.println(set);  //无序,不允许重复

        LinkedHashSet<String> linked = new LinkedHashSet<>();

        linked.add("www");
        linked.add("abc");
        linked.add("abc");
        linked.add("cn");

        System.out.println(linked);  //有序,不允许重复
    }
}

TreeSet

底层数据结构:红⿊树
特点:
基于排列顺序实现元素不重复
实现了 Sortedset 接口,对集合元素自动排序
元素对象的类型必须实现 Comparable 接口,指定排序规则
通过 CompareTo 方法确定是否为重复元素

Collections

public class Collections extends Object

常用方法

数据类型 方法 说明
static <T> boolean addAll(Collection<? super T> c, T... elements) 将所有指定元素添加到指定 collection 中
static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换
static <T extends Comparable<? super T>> <br/>void sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序
static <T> void sort(List<T> list, Comparator<? super T> c) 根据指定比较器产生的顺序对指定列表进行排序
@Data
public class Person implements Comparable<Person>{
    private String name;
    private int age;
    //重写排序规则
    @Override
    public int compareTo(Person o) {
        return  this.getAge() - o.getAge(); //年龄升序
        //return  o.getAge() - this.getAge(); //年龄降序
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

import java.util.ArrayList;
import java.util.Collections;
/*
    注意:sort()使用:在被排序的集合实现必须重写接口中的 compareTo 定义的规则
    compareTo:
        升序:this.参数 - o.参数
        降序:o.参数 - this.参数
 */

public class CollectionsDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        //添加多个元素
        Collections.addAll(list,"a","b","c","d","e");
        System.out.println(list);  //[a, b, c, d, e]

        //打乱元素顺序
        Collections.shuffle(list);
        System.out.println(list);  //[e, b, d, a, c]

        System.out.println("===============================");

        ArrayList<Integer> in = new ArrayList<>();
        Collections.addAll(in,12,15,14,13,16);
        System.out.println(in);  //[12, 15, 14, 13, 16]

        //升序
        Collections.sort(in);
        System.out.println(in);  //[12, 13, 14, 15, 16]

        System.out.println("===============================");

        ArrayList<String> str = new ArrayList<>();
        Collections.addAll(str,"lalala","hefery","hahaha","biubiubiu");
        System.out.println(str);  //[lalala, hefery, hahaha, biubiubiu]
        Collections.sort(str);
        System.out.println(str);  //[biubiubiu, hahaha, hefery, lalala]

        System.out.println("===============================");

        ArrayList<Person> p = new ArrayList<>();
        p.add(new Person("lalala",12));
        p.add(new Person("hahaha",8));
        p.add(new Person("wawawa",32));
        System.out.println(p); //[Person{name='lalala', age=12}, Person{name='hahaha', age=8}, Person{name='wawawa', age=32}]

        //重写 compareTo 方法,根据 age 升序
        Collections.sort(p); //[Person{name='hahaha', age=8}, Person{name='lalala', age=12}, Person{name='wawawa', age=32}]

        System.out.println(p);
    }
}

Comparator 和 Comparable

Comparable:自己 this 与别人 o 比较,自己需要实现 Comparable 接口,重写比较规则 compareTo 方法
Comparator:找第三方仲裁

public class CollectionsDemo {
    public static void main(String[] args) {
        ArrayList<Person> p = new ArrayList<>();
        p.add(new Person("lalala",12));
        p.add(new Person("bhahaha",8));
        p.add(new Person("ahihihi",8));
        p.add(new Person("wawawa",32));
        System.out.println(p);
        //[Person{name='lalala', age=12}, Person{name='hahaha', age=8}, Person{name='wawawa', age=32}]

        //重写 compareTo 方法,根据 age 升序
        //Collections.sort(p);
        //[Person{name='hahaha', age=8}, Person{name='lalala', age=12}, Person{name='wawawa', age=32}]

        /*Collections.sort(p, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //return o1.getAge() - o2.getAge();  //age升序
                return o2.getAge() - o1.getAge();   //age降序
            }
        });*/

        Collections.sort(p, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                int result = o1.getAge() - o2.getAge();
                //age相同,比较姓的首字母
                if( result > 0 ){
                    result = o1.getName().charAt(0) - o2.getName().charAt(0);
                }
                return result;
            }
        });

        System.out.println(p);
    }
}

Map

HashMap<K,V>

底层:数组 + 链表(Java8)、数组 + 链表 + 红黑树(Java8 及之后)
特点:无序,存储取出元素顺序可能不一致

重要方法

public class mapDemo {
    public static void main(String[] args) {
        //show01();
        //show02();
        //show03();
        show04();
    }

    private static void show04() {
        /*
             boolean containsKey(Object key)
                如果此映射包含指定键的映射关系,则返回 true
         */
        Map<String,Integer> map4 = new HashMap<>();

        map4.put("赵丽颖",168);
        map4.put("林志玲",178);
        map4.put("刘洋",188);

        System.out.println(map4);

        boolean v1 = map4.containsKey("赵丽颖");
        System.out.println("v1:" + v1);  //v1:true
        boolean v2 = map4.containsKey("赵颖");
        System.out.println("v1:" + v2);  //v1:false
    }

    private static void show03() {
        /*
             V get(Object key)
                返回指定键所映射的值;
                如果此映射不包含该键的映射关系,则返回 null
         */
        Map<String,Integer> map3 = new HashMap<>();

        map3.put("赵丽颖",168);
        map3.put("林志玲",178);
        map3.put("刘洋",188);

        System.out.println(map3);

        Integer v1 = map3.get("刘洋");
        System.out.println("v1: " + v1);
    }

    private static void show02() {
        /*
             V remove(Object key) :
                返回值:V
                    Key 存在,返回被删除值
                    Key不存在,返回null
         */
        Map<String,Integer> map2 = new HashMap<>();

        map2.put("赵丽颖",168);
        map2.put("林志玲",178);
        map2.put("刘洋",188);

        System.out.println(map2);

        Integer v1 = map2.remove("林志玲");
        System.out.println("v1:" + v1);  //v1:178
        System.out.println(map2);

        Integer v2 = map2.remove("林志颖");
        System.out.println("v1:" + v2);  //v1:null
    }

    private static void show01() {
        /*
            put(K key, V value):
                返回值:V value
                    存储键值对时,Key 不重复,返回值 value 为 null
                    存储键值对时,Key 重复,使用新的 value 替换 map 中的 value
         */
        Map<String,String> map1 = new HashMap<>();

        //测试 Key
        String v1 = map1.put("hefery", "wahaha1");
        System.out.println("v1: " + v1);  //v1: null

        String v2 = map1.put("hefery", "wahaha2");
        System.out.println("v2: " + v2);  //v2: wahaha1

        System.out.println(map1);  //{hefery=wahaha2}

        //测试 value
        map1.put("张杰","谢娜");
        map1.put("李晨","范冰冰");
        map1.put("陈乔恩","范冰冰");

        System.out.println(map1);  //{陈乔恩=范冰冰, hefery=wahaha2, 李晨=范冰冰, 张杰=谢娜}
    }
}
/*
    Map 集合遍历:
        1.键找值
        2.键值对
 */
public class mapDemo {
    public static void main(String[] args) {
        /*Map<String,Integer> map = new HashMap<>();

        map.put("赵丽颖",168);
        map.put("林志玲",178);
        map.put("刘洋",188);

        //1.将所有 Key 取出,存入 Set 集合
        Set<String> set = map.keySet();

        //2.使用迭代器遍历
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            //3.通过 key 找到 value
            String key = it.next();
            Integer value = map.get(key);
            System.out.println(key + " = " + value);
        }

        //2.foreach遍历
        for (String key : set) {
            //3.通过 key 找到 value
            String value = it.next();
            System.out.println(key + " = " + value);
        }

        for (String key : map.keySet()) {
            String value = it.next();
            System.out.println(key + " = " + value);
        }*/

        System.out.println("===================================================");

        Map<String,Integer> map = new HashMap<>();

        map.put("赵丽颖",168);
        map.put("林志玲",178);
        map.put("刘洋",188);

        //1.使用 Map 集合中的 entrySet() 方法,把集合中的多个 Entry 对象取出,存入 Set 集合
        Set<Map.Entry<String,Integer>> set = map.entrySet();

        //2.迭代器遍历
        Iterator<Map.Entry<String, Integer>> it = set.iterator();
        while (it.hasNext()){
            Map.Entry<String, Integer> entry = it.next();
            //3.通过 getKey()、getValue() 得到 key、value 值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }

        //foreach遍历
        for (Map.Entry<String, Integer> entry : set ) {
            //3.通过 getKey()、getValue() 得到 key、value 值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }
    }
}

存储自定义类型的数据

@Data
public class Person{
    private String name;
    private int age;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}

/*
    Map 集合包装 key 唯一:
        作为 key 元素,必须重写 hSHcODE() 和 equlas() 方法
        作为 value 值,可重复
 */
public class mapDemo {
    public static void main(String[] args) {
        show();
    }

    private static void show() {
        //创建 HashMap 集合
        HashMap<String,Person> map = new HashMap<>();
        HashMap<Person,String> map1 = new HashMap<>();

        //添加元素
        map.put("北京",new Person("张三",13));
        map.put("上海",new Person("李四",20));
        map.put("深圳",new Person("王五",10));
        map.put("北京",new Person("麻子",13));

        map1.put(new Person("张三",13),"北京");
        map1.put(new Person("李四",20),"上海");
        map1.put(new Person("王五",10),"深圳");
        map1.put(new Person("张三",13),"南京");

        //遍历
        Set<String> set = map.keySet();
        for (String key : set){
            Person value = map.get(key);
            System.out.println(key + " = " + value);
        }
        //麻子将张三替换

        //使用 entrySet() 和 foreach 遍历
        Set<Map.Entry<Person, String>> set1 = map1.entrySet();
        for (Map.Entry<Person, String> entry : set1) {
            Person key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + " = " + value);
        }

    }
}

源码分析

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {

    private static final long serialVersionUID = 362498820763181265L;
  
    /** 默认初识容量 */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;  // aka 16

    /** 最大容量 */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /** 加载因子 */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /** 当链表长度大于8,并且数组长度大于64,链表转化为红黑树 */
    static final int TREEIFY_THRESHOLD = 8;

    /** 当链表长度小于6,红黑树转化为链表 */
    static final int UNTREEIFY_THRESHOLD = 6;

    /** 转化为红黑树的最小数组长度 */
    static final int MIN_TREEIFY_CAPACITY = 64;

	/** 元素个数 */
    transient int size;
  
    /** 键值对 */
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }
  
    /** 存放键值对Node的数组 */
    transient Node<K,V>[] table;
  
    /** 无参构造 */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
  
  
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
  
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  
    /** HashMap刚创建时, table是nu11,为了节省空间,当添加第一个元素时, table容量调整为16 */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
  
    /** 当元素个数大于阈值(16*8.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数 
        jdk1.8:当每个链表长度大于8,并且元素个数大于等于64时会调整为红黑树,目的提高执行效率
        当链表长度小于6时,调整成链表
        jdk1.8以前,链表头插入,jdk1.8以后链表尾插入*/
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
  
}

LinkedHashMap

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

1.底层:哈希表 + 链表
2.有序,存储取出元素顺序一致

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();

        map.put("a","a");
        map.put("c","c");
        map.put("b","b");
        map.put("a","d");

        System.out.println(map); //key 值不允许重复,无序 {a=d, b=b, c=c}

        System.out.println("==========================================");

        LinkedHashMap<String,String> linked = new LinkedHashMap<>();
        linked.put("a","a");
        linked.put("c","c");
        linked.put("b","b");
        linked.put("a","d");

        System.out.println(linked); //key 值不允许重复,有序 {a=d, c=c, b=b}
    }
}
public class LinkedHashMapDemo {
    public static void main(String[] args) {
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        map.put("邓超", "孙俪");
        map.put("李晨", "范冰冰");
        map.put("刘德华", "朱丽倩");
        Set<Entry<String, String>> entrySet = map.entrySet();
        for (Entry<String, String> entry : entrySet) {
            System.out.println(entry.getKey() + "  " + entry.getValue());
        }
    }
}

ConcurrentHashMap<K,V>

所有操作都是线程安全的,但获取操作不必锁定,并且不支持以某种防止所有访问的方式锁定整个表
此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关

构造函数

构造函数 说明
ConcurrentHashMap() 创建一个带有默认初始容量 (16)、加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射
ConcurrentHashMap(int initialCapacity) 创建一个带有指定初始容量、默认加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射
ConcurrentHashMap(int initialCapacity, float loadFactor) 创建一个带有指定初始容量、加载因子和默认 concurrencyLevel (16) 的新的空映射
ConcurrentHashMap(Map m) 创建一个带有指定初始容量、加载因子和并发级别的新的空映射
ConcurrentHashMap(Map<? extents K, ? extents V> m) 构造一个与给定映射具有相同映射关系的新映射

重要方法

数据类型 方法 说明
V put(K key, V value) 将指定键映射到此表中的指定值
V get(Object key) 返回指定键所映射到的值,如果此映射不包含该键的映射关系,则返回 null
void clear() 从该映射中移除所有映射关系
boolean contains(Object value) 一种遗留方法,测试此表中是否有一些与指定值存在映射关系的键
boolean containsKey(Object key) 测试指定对象是否为此表中的键
boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true
Enumeration elements() 返回此表中值的枚举
Set<Map.Entry<K,V>> entrySet() 返回此映射所包含的映射关系的 Set 视图
boolean isEmpty() 如果此映射不包含键-值映射关系,则返回 true
Enumeration keys() 返回此表中键的枚举
Set keySet() 返回此映射中包含的键的 Set 视图
void putAll(Map<? extends K,? extends V> m) 将指定映射中所有映射关系复制到此映射中
V putIfAbsent(K key, V value) 如果指定键已经不再与某个值相关联,则将它与给定值关联
V remove(Object key) 从此映射中移除键(及其相应的值)
boolean remove(Object key, Object value) 只有目前将键的条目映射到给定值时,才移除该键的条目
V replace(K key, V value) 只有目前将键的条目映射到某一值时,才替换该键的条目
boolean replace(K key, V oldValue, V newValue) 只有目前将键的条目映射到给定值时,才替换该键的条目
int size() 返回此映射中的键-值映射关系数
Collection values() 返回此映射中包含的值的 Collection 视图
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...