跳到主要内容
  1. 所有文章/

Java集合相关笔记

点击查看笔记列表

集合类框架图 #

Java集合类主要由两个根接口CollectionMap派生出来的,Collection派生出了三个子接口:List、Set、Queue(Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。

image-20220221145833541.png

image-20220221145400138.png

线程安全与线程不安全的集合 #

线程安全的:

image-20220221145750184.png

  • Hashtable:比HashMap多了个线程安全。
  • ConcurrentHashMap:是一种高效但是线程安全的集合。
  • Vector:比Arraylist多了个同步化机制。
  • Stack:栈,也是线程安全的,继承于Vector。

线性不安全的:

  • HashMap
  • Arraylist
  • LinkedList
  • HashSet
  • TreeSet
  • TreeMap

Collection集合实现比较要怎么做? #

第一种,实体类实现Comparable接口,并实现 compareTo(T t) 方法,称为内部比较器。

import java.util.TreeSet;
public class Compare {
    public static void main(String[] args) {
        TreeSet<Student> students=new TreeSet<>();
        students.add(new Student(3,"C"));
        students.add(new Student(2,"B"));
        students.add(new Student(1,"A"));
        students.forEach(System.out::println);
    }
}
class Student implements Comparable<Student>{
    int age;
    String name;
    public Student(int age,String name){
        this.age=age;
        this.name=name;
    }
    @Override
    public int compareTo(Student o) {
        //标准升序排序:大于  1   小于  -1  等于  0
        return (Integer.compare(this.age, o.age));
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

第二种,创建一个外部比较器,这个外部比较器要实现Comparator接口的 compare(T t1, T t2)方法。

import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.TreeSet;
public class Compare {
    public static void main(String[] args) {
        //升序
        TreeSet<Student> students=new TreeSet<>(Comparator.comparingInt(o -> o.age));
        students.add(new Student(3,"C"));
        students.add(new Student(2,"B"));
        students.add(new Student(1,"A"));
        students.forEach(System.out::println);
    }
}
class Student{
    int age;
    String name;
    public Student(int age,String name){
        this.age=age;
        this.name=name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

Iterator 和 ListIterator 有什么区别? #

  • 遍历。使用Iterator,可以遍历所有集合,如Map,List,Set;但只能在向前方向上遍历集合中的元素。

使用ListIterator,只能遍历List实现的对象,但可以向前和向后遍历集合中的元素。

public static void main(String[] args) {
        // 创建集合
        ArrayList<String> sites = new ArrayList<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        // 获取迭代器
        Iterator<String> it = sites.iterator();
        ListIterator<String> its=sites.listIterator();
        // 输出集合中的所有元素
        while(it.hasNext()) {
            System.out.println(it.next());
        }
    }
public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();     //检查是否有下一个元素
    E next();              //返回下一个元素
    boolean hasPrevious(); //check是否有previous(前一个)element(元素)
    E previous();          //返回previous element
    int nextIndex();       //返回下一element的Index
    int previousIndex();   //返回当前元素的Index
    void remove();         //移除一个elment
    void set(E e);         //set()方法替换访问过的最后一个元素 注意用set设置的是List列表的原始值
    void add(E e);         //添加一个element
}

public interface Iterator<E> {
    boolean hasNext();     //检查是否有下一个元素
    E next();              //返回下一个元素
}
  • 添加元素。Iterator无法向集合中添加元素;而ListIteror可以向集合添加元素。
  • 修改元素。Iterator无法修改集合中的元素;而ListIterator可以使用set()修改集合中的元素。
  • 索引。Iterator无法获取集合中元素的索引;而使用ListIterator,可以获取集合中元素的索引。

快速失败(fail-fast)和安全失败(fail-safe) #

快速失败(fail—fast)

  • 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception
  • 原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
  • 注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
  • 场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),比如HashMap、ArrayList 这些集合类。

安全失败(fail—safe)

  • 采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
  • 原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
  • 缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
  • 场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改,比如:ConcurrentHashMap。

笔记列表 #

ConcurrentHashMap相关

·📄 3305 字·🍵 7 分钟
JDK1.7中的ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成,即ConcurrentHashMap 把哈希桶切分成小数组(Segment ),每个小数组有 n 个 HashEntry 组成。

ArrayList相关

·📄 3264 字·🍵 7 分钟
ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;

HashMap相关

· 📖 2 篇 · 🕘 15 分钟
在JDK1.7 中,由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。在JDK1.8 中,由“数组+链表+红黑树”组成。