数据结构

  • Java常见的集合数据

数据结构的说明

数组

  • 无序数组
    • 优点:查询快,如果知道索引可以快速地存取
    • 缺点:删除慢,大小固定
  • 有序数组
    • 优点:比无序数组查找快
    • 缺点:删除和插入慢,大小固定

  • 优点:提供后进先出的存取方式
  • 缺点:存取其他项很慢
  • 比如,Android中管理activity进出就是使用栈

队列

  • 优点:提供先进先出的存取方式
  • 缺点:存取其他项都很慢

链表

  • 优点:插入快,删除快
  • 缺点:查找慢(一个个节点查)

二叉树

  • 优点:查找,插入,删除都快(平衡二叉树)
  • 缺点:删除算法复杂

红-黑树

  • 优点:查找,插入,删除都快,树总是平衡的(局部调整)
  • 缺点:算法复杂

哈希表

  • 优点:如果关键字已知则存取速度极快,插入快
  • 缺点:删除慢,如果不知道关键字则存取很慢,对存储空间使用不充分

  • 优点:插入,删除快,对最大数据的项存取很快
  • 缺点:对其他数据项存取很慢

  • 优点:对现实世界建模
  • 缺点:有些算法慢且复杂

集合

为什么出现集合类?

  • 为了方便对多个对象进行操作,我们就必须把这多个对象进行存储。而要想存储多个对象,就不能是一个基本的变量,而应该是一个容器类型的变量,
  • 在我们目前所学过的知识里面,有哪些是容器类型有数组和StringBuffer。
    • StringBuffer的结果是一个字符串,不一定满足我们的要求,所以我们只能选择数组,这就是对象数组。
    • 而对象数组又不能适应变化的需求,因为数组的长度是固定的,这个时候,为了适应变化的需求,Java就提供了集合类供我们使用。

数组与集合的区别

  • 长度区别

    • 数组的长度固定
    • 集合长度可变
  • 内容不同

    • 数组存储的是同一种类型的元素

    • 而集合可以存储不同类型的元素

  • 元素的数据类型问题

    • 数组可以存储基本数据类型,也可以存储引用数据类型

    • 集合只能存储引用类型

    • 性能上

      • 数组更好
      • 集合底层数据结构复杂

集合继承树

Set集合

  • 一般使用的有TreeSet和HashSet,数据不能重复

  • TreeSet

    • 基于红黑树实现,支持有序性操作,放入数据不能重复且不能为null
    • 但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
    • 可以重写compareTo()方法来确定元素大小,从而进行升序排序。
  • HashSet

    • HashSet是根据hashCode来决定存储位置的,是通过HashMap实现的(map(值,null)),所以对象必须实现hashCode()方法,
    • 存储的数据无序不能重复,可以存储null,但是只能存一个。
    • 查找的时间复杂度为 O(1),
  • LinkedHashSet:

    • 具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。

List集合

  • List比较常用的有ArrayList和LinkedList,还有一个比较类似的Vector

  • ArrayList

    • 是使用动态数组来实现的,对于数据的随机get和set或是少量数据的插入或删除,支持随机访问,效率会比较高。
    • ArrayList是线程不安全的,在不考虑线程安全的情况下速度也比较快的。
    • ArrayList插入数据可以重复,也是有序的,按照插入的顺序来排序。
    • 根据序号读取数据只需直接获取数组对应脚表的数据
  • LinkedList

    • 内部是基于双向链表实现的形式来实现的,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。。
    • LinkedList根据序号获取数据,是二分进行遍历,如果序号小于总长度的一半,就从链表头部开始往后遍历,直到找到对应的序号。如果序号大于总长度的一半,就从链表尾部往前进行遍历,直到找到对应的序号。拿到数据。
  • Vector

    • Vector的使用方法和内部实现基本和ArrayList相同,只不过它在add(), remove(), get()等方法中都加了同步。所以它是线程安全的。但是使用效率上就不如ArrayList了。

Map集合

  • HashMap

    • HashMap是基于哈希表来实现的,简单的来说,根据key算出一个hash值,确定一个存放index,但是hash值有可能会冲突重复,所以如果冲突的hash值就需要以链表的形式在同一个index存放了。
  • TreeMap

    • TreeMap的使用大致跟HashMap类似,但是内部实现是根据红黑树来实现的。红黑树是一种平衡有序的二叉树,TreeMap的插入删除查询都是依据红黑树的规则来进行的。
  • HashTable

    • HashMap和TreeMap都是线程不安全的,多线程操作的时候可能会造成数据错误。Hashtable是线程安全的。其他内部实现,与HashMap都是一样的。
    • 意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。
    • 现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
  • LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

Java 集合框架中的队列 Queue

  • 什么是队列

    • 队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列),跟我们生活中的排队类似。
  • 队列的种类

    • 单队列(单队列就是常见的队列, 每次添加元素时,都是添加到队尾,存在“假溢出”的问题也就是明明有位置却不能添加的情况)
    • 循环队列(避免了“假溢出”的问题)
  • Java 集合中的 Queue 继承自 Collection 接口 ,Deque, LinkedList, PriorityQueue, BlockingQueue 等类都实现了它。

    • Queue 用来存放 等待处理元素 的集合,这种场景一般用于缓冲、并发访问。除了继承 Collection 接口的一些方法,Queue 还添加了额外的 添加、删除、查询操作。
    • LinkedList:可以用它来实现双向队列。
    • PriorityQueue:基于堆结构实现,可以用它来实现优先队列。

Collection接口

java.util.Collection下的接口和继承类关系简易结构图:

  • 存储单列数据,可以重复,无序

常用的方法

方法名 描述
size() 查看集合的长度
boolean add(Object obj) 往集合中添加元素
boolean addAll(Object obj) 将集合中的所有元素添加到当前集合中,只要当前集合发生改变true
void clear() 清除集合中的所有元素
boolean remove(Object obj) 将元素添从当前集合中删除,只要当前集合发生改变true
boolean removeAll(Collection c) 将集合中的所有元素从当前集合中删除
removeIf() 带条件删除,重写Predicate接口的test方法
boolean contains(Object obj) 查看元素是否在集合中,有一个就是true
boolean containsAll(Collection c) 判断集合元素是否全部在当前集合中,全部在为true
boolean retainAll(Collecton c) 判断交集,返回值表示的是集合是否发生过改变。
isEmpty() 查看集合是否为空
public T[] toArray(T[] a) 将集合变为数组
Arrays.asList(String[] str) 数组转集合,不能进行集合长度改变的操作(增加,删除),UnsupportedOperationException
  • 演示collection的功能

    • 集合对象的创建需要通过接口的实现类
    • Collection c = new Collection(); //错误,因为接口不能实例化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    public static void main(String[] args) {
    Collection<String> c = new ArrayList<>();
    System.out.println("isEmpty:" + c.isEmpty());
    // 添加数据
    c.add("aa");
    c.add("bb");
    c.add("cc");

    // 查看集合的长度
    System.out.println("size:" + c.size()); // size:3

    // 遍历
    for (String s : c) {
    System.out.print(s + " "); // aa bb cc
    }

    System.out.println();

    // 添加集合addAll(集合)
    c.addAll(c);
    System.out.println("addAll:" + c);// addAll:[aa, bb, cc, aa, bb, cc]

    // 移除remove(object)
    c.remove("aa");
    System.out.println("remove:" + c);// remove:[bb, cc, aa, bb, cc]

    // 移除集合
    Collection<String> d = new ArrayList<>();
    d.add("aa");
    d.add("bb");
    c.removeAll(d);
    System.out.println("removeAll:" + c); // removeAll:[cc, cc]

    // 带条件删除removeIf
    c.add("abc");
    // c.removeIf(new Predicate<String>() {
    //
    // @Override
    // public boolean test(String t) {
    // return t.length()<=2;
    // }
    // });

    // lambda 表达式
    c.removeIf((t) -> t.length() <= 2);
    System.out.println("removeIf:" + c); // removeIf:[abc]

    // 是否存在参数指定的元素,存在true
    System.out.println("contains:" + c.contains("abc")); // contains:true

    // 是否存在集合参数中的全部元素,全部存在为true
    c.add("aa");
    System.out.println("containsAll:" + c.containsAll(d));// containsAll:false,只有aa包含

    // 数组转换成集合,Arrays.asList是Arrays的静态内部类
    // 不能添加,移除(改变长度)
    List<String> list = Arrays.asList(new String[] { "aa", "bb" });
    // list.add("cc"); //UnsupportedOperationException
    // list.remove("aa"); // UnsupportedOperationException
    System.out.println("get:" + list.get(1)); // get:bb

    // 集合转数组toArray
    Object[] array = d.toArray(new Object[2]);
    // Object[] array = d.toArray();
    String[] array2 = d.toArray(new String[3]);

    // 遍历
    System.out.println("toArray:" + Arrays.toString(array));// toArray:[aa, bb, null]

    //移除所有元素
    c.clear();
    System.out.println("clear:" + c);//clear:[]
    }
  • 交集

    • 假设有两个集合A,B。
    • A对B做交集,最终的结果保存在A中,B不变。
    • 返回值表示的是A是否发生过改变。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    System.out.println("retainAll:"+c1.retainAll(c2));
    System.out.println("c1:" + c1);
    System.out.println("c2:" + c2);
    --------------------------------------------------
    结果:
    retainAll:false
    c1:[abc1, abc2, abc3, abc4]
    c2:[abc1, abc2, abc3, abc4, abc5, abc6, abc7]

    System.out.println("retainAll:"+c2.retainAll(c1));
    System.out.println("c1:" + c1);
    System.out.println("c2:" + c2);
    --------------------------------------------------
    结果:
    retainAll:true
    c1:[abc1, abc2, abc3, abc4]
    c2:[abc1, abc2, abc3, abc4]

集合的遍历

转成数组

  • 集合转成数组然后遍历

    • Object[] toArray():把集合转成数组,可以实现集合的遍历
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 创建集合对象
    Collection c = new ArrayList();

    // 添加元素
    c.add("hello"); // Object obj = "hello"; 向上转型
    c.add("world");
    c.add("java");

    // 遍历
    // Object[] toArray():把集合转成数组,可以实现集合的遍历
    Object[] objs = c.toArray();
    for (int x = 0; x < objs.length; x++) {
    // System.out.println(objs[x]);

    // 元素是字符串,获取到元素的的同时,还想知道元素的长度。
    // System.out.println(objs[x] + "---" + objs[x].length());

    // 上面的实现不了,原因是Object中没有length()方法
    // 我们要想使用字符串的方法,就必须把元素还原成字符串

    // 向下转型
    String s = (String) objs[x];
    System.out.println(s + "---" + s.length());
    }

    结果:
    hello---5
    world---5
    java---4
  • 对象集合的遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // 创建集合对象
    Collection c = new ArrayList();

    // 创建学生对象
    Student s1 = new Student("花绛沁", 27);
    Student s2 = new Student("花锦瑟", 30);
    Student s3 = new Student("尹幽妍", 33);
    Student s4 = new Student("上官柳丝", 25);
    Student s5 = new Student("蝶恋花", 22);

    // 把学生添加到集合
    c.add(s1);
    c.add(s2);
    c.add(s3);
    c.add(s4);
    c.add(s5);

    // 把集合转成数组
    Object[] objs = c.toArray();
    // 遍历数组
    for (int x = 0; x < objs.length; x++) {
    // System.out.println(objs[x]);
    //向下转型
    Student s = (Student) objs[x];
    System.out.println(s.getName() + "---" + s.getAge());
    }
    结果:
    花绛沁---27
    花锦瑟---30
    尹幽妍---33
    上官柳丝---25
    蝶恋花---22

迭代器

  • Iterator iterator():迭代器,集合的专用遍历方式

    • Object next():获取元素,并移动到下一个位置。
    • NoSuchElementException:没有这样的元素,因为你已经找到最后了。
    • boolean hasNext():如果仍有元素可以迭代,则返回 true。
  • 基本对象集合的遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 创建集合对象
    Collection c = new ArrayList();

    // 创建并添加元素
    // String s = "hello";
    // c.add(s);

    c.add("hello");
    c.add("world");
    c.add("java");

    // Iterator iterator():迭代器,集合的专用遍历方式
    Iterator it = c.iterator(); // 实际返回的肯定是子类对象,这里是多态

    while (it.hasNext()) {
    // System.out.println(it.next());

    String s = (String) it.next();
    System.out.println(s);
    }

    结果:
    hello
    world
    java
  • 对象集合的遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // 创建集合对象
    Collection c = new ArrayList();

    // 创建学生对象
    Student s1 = new Student("花绛沁", 27);
    Student s2 = new Student("花锦瑟", 30);
    Student s3 = new Student("尹幽妍", 33);
    Student s4 = new Student("上官柳丝", 25);
    Student s5 = new Student("蝶恋花", 22);

    // 把学生添加到集合
    c.add(s1);
    c.add(s2);
    c.add(s3);
    c.add(s4);
    c.add(s5);

    //遍历
    Iterator it = c.iterator();
    while(it.hasNext()){
    Student s = (Student)it.next();
    System.out.println(s.getName() + "---" + s.getAge());
    }
    结果:
    花绛沁---27
    花锦瑟---30
    尹幽妍---33
    上官柳丝---25
    蝶恋花---22


    注意: NoSuchElementException 不要多次使用it.next()方法
    System.out.println(((Student) it.next()).getName() +
    "---" + ((Student) it.next()).getAge());

    结果:
    花绛沁---30
    尹幽妍---25
    Exception in thread "main" java.util.NoSuchElementException

for的注意点

1
2
3
4
5
6
7
8
9
10
11
12
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}

Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + ",");
}

for (Integer i : list) {
System.out.print(i + ",");
}
  • 第一种是普通的for循环遍历

  • 第二种是使用迭代器进行遍历

  • 第三种我们一般称之为增强for循环(for each)

    可以看到,第三种形式是JAVA提供的语法糖,这里我们剖洗一下,这种增强for循环底层是如何实现的。

    • 反编译后:
    1
    2
    3
    4
    Integer i;
    for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
    i = (Integer)iterator.next();
    }
  • 如果在Vector,Collections.synchronizedList使用增强for循环,就必须在外面单独加锁,因为它不是单单一个操作,不是原子性的,如果在遍历的过程中,进行add,remove操作,就会抛出异常。

Collections工具类

  • Collections:是针对集合进行操作的工具类,有对集合进行排序和二分查找的方法,都是静态方法,。
  • Collection:是单列集合的顶层接口,有子接口List和Set。

成员方法

方法 描述
public static <T> void sort(List<T> list) 排序 默认情况下是自然顺序。
public static <T> int binarySearch(List<?> list,T key) 二分查找
public static <T> T max(Collection<?> coll) 最大值
public static void reverse(List<?> list) 反转
public static void shuffle(List<?> list) 随机置换
static class SynchronizedList<E> 将非安全集合包装成一个线程安全的
sort(集合,比较器) 自定义排序方法
min(list) 集合中的最小元素
frequency(list,e) 查找参数元素e在集合元素中出现的次数,不存在为0
fill (list,e) 用元素e填充list
  • 代码测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    //
    Collections.addAll(list, "aa","cc","bb");
    System.out.println(list);

    //自然 升序
    Collections.sort(list);
    System.out.println(list);

    //指定比较器
    // Collections.sort(list, (s1,s2)->s2.compareTo(s1));
    System.out.println(list);

    //查找参数 元素 在集合中 出现的索引 , 前提 升序 排序
    // System.out.println(Collections.binarySearch(list, "aaa"));

    //集合 中 最小 的 和 最大
    System.out.println(Collections.min(list));
    System.out.println(Collections.max(list));
    //
    list.add("aa");
    System.out.println(list);

    //查找 参数 元素 在集合 中 出现的 次数 ,不存在 0
    System.out.println(Collections.frequency(list, "aa"));//2

    //对集合元素进行反转
    Collections.reverse(list);
    System.out.println(list);

    //集合元素的洗牌
    Collections.shuffle(list);
    System.out.println(list);

    //集合的填充 ,用 参数 来替换 集合 中的每个元素。
    Collections.fill(list, "xxx");
    System.out.println(list);

    }

比较器排序

  • static void sort(List list, Comparator<? super T> c)

  • 根据指定的比较器指定的顺序对指定的列表进行排序,如果同时有自然排序和比较器排序,以比较器排序为主

    1
    2
    3
    4
    5
    6
    7
    8
    Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
    int num = s1.getAge() - s2.getAge();
    int num2 = num == 0 ? s1.getName().compareTo(s2.getName()): num;
    return num2;
    }
    });

自然排序

  • 对象排序要实现 implements Comparable接口,重写compareTo 方法

    1
    2
    3
    4
    5
    6
    @Override
    public int compareTo(Student s) {
    int num = this.age - s.age;
    int num2 = num == 0 ? this.name.compareTo(s.name) : num;
    return num2;
    }

模拟斗地主

  • 思路:

    1
    2
    3
    4
    5
    6
    7
    8
    A:创建一个HashMap集合
    B:创建一个ArrayList集合
    C:创建花色数组和点数数组
    D:从0开始往HashMap里面存储编号,并存储对应的牌
    同时往ArrayList里面存储编号即可。
    E:洗牌(洗的是编号)
    F:发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
    G:看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    public static void main(String[] args) {
    // 创建一个HashMap集合
    HashMap<Integer, String> hm = new HashMap<Integer, String>();

    // 创建一个ArrayList集合
    ArrayList<Integer> array = new ArrayList<Integer>();

    // 创建花色数组和点数数组
    // 定义一个花色数组
    String[] colors = { "♠", "♥", "♣", "♦" };
    // 定义一个点数数组
    String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q",
    "K", "A", "2", };

    // 从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里面存储编号即可。
    int index = 0;

    for (String number : numbers) {
    for (String color : colors) {
    String poker = color.concat(number);
    hm.put(index, poker);
    array.add(index);
    index++;
    }
    }
    hm.put(index, "小王");
    array.add(index);
    index++;
    hm.put(index, "大王");
    array.add(index);

    // 洗牌(洗的是编号)
    Collections.shuffle(array);

    // 发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
    TreeSet<Integer> fengQingYang = new TreeSet<Integer>();
    TreeSet<Integer> linQingXia = new TreeSet<Integer>();
    TreeSet<Integer> liuYi = new TreeSet<Integer>();
    TreeSet<Integer> diPai = new TreeSet<Integer>();

    for (int x = 0; x < array.size(); x++) {
    if (x >= array.size() - 3) {
    diPai.add(array.get(x));
    } else if (x % 3 == 0) {
    fengQingYang.add(array.get(x));
    } else if (x % 3 == 1) {
    linQingXia.add(array.get(x));
    } else if (x % 3 == 2) {
    liuYi.add(array.get(x));
    }
    }

    // 看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
    lookPoker("张三", fengQingYang, hm);
    lookPoker("李四", linQingXia, hm);
    lookPoker("王五", liuYi, hm);
    lookPoker("底牌", diPai, hm);
    }

    // 写看牌的功能
    public static void lookPoker(String name, TreeSet<Integer> ts,
    HashMap<Integer, String> hm) {
    System.out.print(name + "的牌是:");
    for (Integer key : ts) {
    String value = hm.get(key);
    System.out.print(value + " ");
    }
    System.out.println();
    }

    张三的牌是:♠A ♦A ♣3445677899 ♥J ♦J ♥Q ♠K 小王
    李四的牌是:♥A ♣A ♥2233445577891010 ♣Q
    王五的牌是:♠22366688910 ♠J ♠Q ♦Q ♥K ♣K ♦K 大王
    底牌的牌是:♠510 ♣J

SynchronizedList

类具体代码

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    static class SynchronizedList<E>
    extends SynchronizedCollection<E>
    implements List<E> {
    private static final long serialVersionUID = -7754090372962971524L;

    final List<E> list;

    SynchronizedList(List<E> list) {
    super(list);
    this.list = list;
    }
    SynchronizedList(List<E> list, Object mutex) {
    super(list, mutex);
    this.list = list;
    }

    public boolean equals(Object o) {
    if (this == o)
    return true;
    synchronized (mutex) {return list.equals(o);}
    }
    public int hashCode() {
    synchronized (mutex) {return list.hashCode();}
    }

    public E get(int index) {
    synchronized (mutex) {return list.get(index);}
    }
    public E set(int index, E element) {
    synchronized (mutex) {return list.set(index, element);}
    }
    public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
    }
    public E remove(int index) {
    synchronized (mutex) {return list.remove(index);}
    }

    public int indexOf(Object o) {
    synchronized (mutex) {return list.indexOf(o);}
    }
    public int lastIndexOf(Object o) {
    synchronized (mutex) {return list.lastIndexOf(o);}
    }

    public boolean addAll(int index, Collection<? extends E> c) {
    synchronized (mutex) {return list.addAll(index, c);}
    }

    public ListIterator<E> listIterator() {
    return list.listIterator(); // Must be manually synched by user
    }

    public ListIterator<E> listIterator(int index) {
    return list.listIterator(index); // Must be manually synched by user
    }

    public List<E> subList(int fromIndex, int toIndex) {
    synchronized (mutex) {
    return new SynchronizedList<>(list.subList(fromIndex, toIndex),
    mutex);
    }
    }

    @Override
    public void replaceAll(UnaryOperator<E> operator) {
    synchronized (mutex) {list.replaceAll(operator);}
    }
    @Override
    public void sort(Comparator<? super E> c) {
    synchronized (mutex) {list.sort(c);}
    }

    private Object readResolve() {
    return (list instanceof RandomAccess
    ? new SynchronizedRandomAccessList<>(list)
    : this);
    }
    }

使用方式

  • 官方文档就是下面的使用方式

    1
    2
    3
    4
    5
    6
    7
    CopyList list = Collections.synchronizedList(new ArrayList());
    ...
    synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
    foo(i.next());
    }

既然封装类内部已经加了对象锁,为什么外部还要加一层对象锁?

  • 看源码可知,Collections.synchronizedList中很多方法,比如equals,hasCode,get,set,add,remove,indexOf,lastIndexOf……都添加了锁,

  • 但是List中CopyIterator<E> iterator();这个方法没有加锁,不是线程安全的,所以如果要遍历,还是必须要在外面加一层锁。

  • 使用Iterator迭代器的话,似乎也没必要用Collections.synchronizedList的方法来包装了——反正都是必须要使用Synchronized代码块包起来的。

  • 所以总的来说,Collections.synchronizedList这种做法,适合不需要使用Iterator、对性能要求也不高的情况。

SynchronizedList和Vector最主要的区别

  1. Vector扩容为原来的2倍长度,ArrayList扩容为原来1.5倍
  2. SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。
  3. 使用SynchronizedList的时候,进行遍历时要手动进行同步处理 。
  4. SynchronizedList可以指定锁定的对象。

**

总结

  • Java带有一组接口和类,使得操作成组的对象更为容易,这就是集合框架

    • 集合框架主要用到的是Collection接口,Collection是将其他对象组织到一起的一个对象,提供了一种方法来存储、访问和操作其元素

    • List、Set和Queue是Collection的三个主要的子接口。此外,还有一个Map接口用于存储键值对

      接口 描述
      Collection Collection是最基本的集合接口,一个 Collection 代表一组Object,Java不提供直接继承自Collection的类,只提供继承于它的子接口
      List List接口是一个有序的Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引来访问List中的元素,而且允许有相同的元素
      Set Set具有与Collection完全一样的接口,只是行为上不同,Set不保存重复的元素
      Queue Queue通过先进先出的方式来存储元素,即当获取元素时,最先获得的元素是最先添加的元素,依次递推
      SortedSet 继承于Set保存有序的集合
      Map 将唯一的键映射到值
      Map.Entry 描述在一个Map中的一个元素(键/值对),是一个Map的内部类
      SortedMap 继承于Map,使Key保持在升序排列