在我们编写代码的过程中会发现有些东西并不适合于用数组来直接装入,这时候我们都会选择我们的集合来完成这个储存的功能,那么我们今天来说一说我们有哪些用的多的集合。
> 先说一下集合的原理,其实就是将一些数组封装好我们直接用就好啦,那么,又有那些集合呢?List,Set,Map相信大家应该不会陌生,这些是我们常用的一集合,List和Set又有一个共同的特点,就是它们同在collection这个接口之下,二Map不一样,它是单独的Map。下面我们先重点说一说List集合

在介绍List之前我们先了解一下UML图
UML-Unified Modeling Language 统一建模语言,又称标准建模语言。是用来对软件密集系统进行可视化建模的一种语言。UML的定义包括UML语义和UML表示法两个元素。
UML是在开发阶段,说明、可视化、构建和书写一个面向对象软件密集系统的制品的开放方法。最佳的应用是工程实践,对大规模,复杂系统进行建模方面,特别是在软件架构层次,已经被验证有效。统一建模语言(UML)是一种模型化语言。模型大多以图表的方式表现出来。一份典型的建模图表通常包含几个块或框,连接线和作为模型附加信息之用的文本。这些虽简单却非常重要,在UML规则中相互联系和扩展。
上面的图就是UML图
UML图的几种关系,(在面试可能遇到)
泛化(Generalization), 实现(Realization), 依赖(Dependence),关联(Association),聚合(Aggregation) ,组合(Composition)
也就是继承:表示is-a的关系,是对象之间耦合度最大的一种关系,子类继承父类(或者是子接口继承父接口),在Java中使用extends关键字来表示,在类图中使用带三角箭头的实线表示,箭头从子类指向父类。
指的是两个实体之间的一种合同关系,一个实体定义一个合同,而另外一个实体保证履行该合同,这就对应于Java中的一个类实现了一个接口,在Java中使用implements 关键字来表示,在类图上,使用虚线 + 空心三角形的方法来表示,箭头从实现类指向接口

可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、、临时性的、非常弱的,但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类B作为参数被类A在某个method方法中使用,或者类A引用了类B的静态方法;在类图上,使用虚线 + 箭头来表示,箭头的方向,从依赖的类指向被依赖的类

就是对象之间的一种依赖关系,比如客户类和订单类之间的关系,这种关系通常使用类的属性表达。关联又分为一般关联、聚合关联与组合关联. 表现在代码层面,为被关联类B以类属性的形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量,在类图使用带箭头的实线表示,箭头从使用类指向被关联的类 可以是单向和双向
聚合算是关联的一种形式,表示has-a的关系,是一种不稳定的包含关系。较强于一般关联,有整体与局部的关系,并且没有了整体,局部也可单独存在。如公司和员工的关系,公司包含员工,但如果公司倒闭,员工依然可以换公司。在类图使用空心的菱形表示,菱形在整体的那一侧

组合(Composition):表示contains-a的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。如公司和部门的关系,没有了公司,部门也不能存在了;调查问卷中问题和选项的关系;订单和订单选项的关系。在类图使用实心的菱形表示,菱形从局部指向整体。
整体类图如下:

特点:元素有序,且可重复
遍历:下标,foreach,迭代器
迭代器:是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合,terator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。

>初始容量10,负载因子0.5,扩容增量0.5倍,新容量 = 原容量 + 原容量 * 0.5 , 如 ArrayList的容量为10,一次扩容后是容量为15。
List实现类:
arraylist,LinkedList,Vector,CopyOnWriteArrayList。
特点:简单数据结构,超出容量自动扩容,动态数组;内部实现是基于基础的对象数组的;随机访问快;不适合随机增加或删除;array list是Java中遍历速度最快的。
假设一组数据有24个元素,现在用list集合来装,当我们不知道有24个元素时让集合自动扩容,当容量到达默认容量时,在第十一个时开始扩容,容量增长为5,容量即为15,当又到达最大容量时,并且还有元素要进来,又开始自动增长,容量增长到为22,然后又到大最大容量继续扩容,到没有数据元素截至。当我们已知数据元素长度,我们就可以指定默认容量,就不会像上面一样继续增长,浪费空间。
注意:arraylist所谓的线程不安全是在类成员变量之中,在我们的方法的之中就不会存在线程安不安全的问题。
数据准备:为方便演示,需要有紧挨在一起的两个或多个相同的元素
- package com.zking.test;
-
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class Tesylist {
- List<Integer> list;
-
- @Before
- public void setup() {
- list = new ArrayList<Integer>();
- list.add(1);
- list.add(4);
- list.add(8);
- list.add(8);
- list.add(12);
- list.add(16);
- }
-
- /**
- * 删除元素为8的元素
- */
-
- @Test
- public void listDel01() {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) == 8)
- list.remove(i);
- }
- System.out.println(list);
- // 结果:[1,4,8,12,16],这种方式直接找到第一个元素8,找到后直接删除结束
-
- }
-
- @Test
- public void listDel02() {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) == 8)
- list.remove(i--);
- }
- System.out.println(list);
- // 结果:[1,4,12,16] 这种方式找到第一个8 删除,但第二个8同时发生移位,
- // 直接跳到第一个8 的位置,但此时指针还未移动,所以又删除一个8 后才往下据继续移动
-
- }
-
- @Test
- public void listDel03() {
- for (int i = list.size() - 1; i >= 0; i--) {
- if (list.get(i) == 8) {
- list.remove(i);
- }
- }
- System.out.println(list);
- // 结果:[1,4,12,16] 这种方式是将集合到过来进行遍历删除,第二个8发生移位但又移动
- // 到了指针还为移动位置,然后删除了
-
- }
-
- @Test
- public void listDel04() {
- for (Integer i : list) {
- if (i == 8)
- list.remove(i);
- }
- System.out.println(list);
- // 结果:执行错误
-
- }
-
- @Test
- public void listDel05() {
- Iterator<Integer> it = list.iterator();
- while (it.hasNext()) {
- if (it.next() == 8) {
- it.remove();
- }
- }
- System.out.println(list);
- // 结果:[1,4,12,16]迭代器
-
- }
-
- @Test
- public void listDel06() {
- Iterator<Integer> it = list.iterator();
- while (it.hasNext()) {
- Integer value = it.next();
- if (value == 8) {
- list.remove(value);
- }
- }
- System.out.println(list);
- // 结果:执行不通过
- }
-
- @Test
- public void listDel07() {
- // ist.add(1);
- // list.add(4);
- // list.add(8);
- // list.add(8);
- // list.add(12);
- // list.add(16);
-
- list.add(4);
- // 结果: [1,4,8,12,16],在这里,删除的是下标为4的元素,刚好为元素8
- list.remove(Integer.valueOf(4));
- System.out.println(list);
- // 结果: [1,8,8,12,16],在这里,删除的是元素为4的元素,以为通过
- // integer.valueof()将4转成了一个对象4,所以删除的是4而不是下标为4的元素
- }
- }
总结:通过上面的案例,我们可以知道,原来araaylist的删除还这么多的方法。
- @Test
- public void listkr() throws Exception {
- List<Integer> list1 = new ArrayList<Integer>();
- for (int i = 0; i < 100; i++) {
- list1.add(i);
- System.out.println("i:" + i);
- System.out.println("len:" + getlistsize(list1));
- }
- }
-
- private int getlistsize(List obj) throws Exception {
- Class<? extends List> clazz = obj.getClass();
- Field f = clazz.getDeclaredField("elementData");
- f.setAccessible(true);
- Object[] object = (Object[]) f.get(obj);
- return object.length;
- }
-
- //运行结果
- i:0
- len:10
- i:1
- len:10
- i:2
- len:10
- i:3
- len:10
- i:4
- len:10
- i:5
- len:10
- i:6
- len:10
- i:7
- len:10
- i:8
- len:10
- i:9
- len:10
- i:10
- len:15
- i:11
- len:15
- i:12
- len:15
- i:13
- len:15
- i:14
- len:15
- i:15
- len:22
- i:16
- len:22
- i:17
- len:22
- i:18
- len:22
- i:19
- len:22
- i:20
- len:22
- i:21
- len:22
- i:22
- len:33
- i:23
- len:33
- i:24
- len:33
- i:25
- len:33
- i:26
- len:33
- i:27
- len:33
- i:28
- len:33
- i:29
- len:33
- i:30
- len:33
- i:31
- len:33
- i:32
- len:33
- i:33
- len:49
- i:34
- len:49
- i:35
- len:49
- i:36
- len:49
- i:37
- len:49
- i:38
- len:49
- i:39
- len:49
- i:40
- len:49
- i:41
- len:49
- i:42
- len:49
- i:43
- len:49
- i:44
- len:49
- i:45
- len:49
- i:46
- len:49
- i:47
- len:49
- i:48
- len:49
- i:49
- len:73
- i:50
- len:73
- ...............
-
-
总结:通过这个案例我们就可以更清晰的认识到array list的扩容机制
新容量 = 原容量 + 原容量 * 0.5
下面是LinkedList的一个概略图模型图

1. LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部
2. 线程不安全
3. LinkedList可被用作堆栈(stack)【包括了push,pop方法】,队列(queue)或双向队列(deque)
4. 以双向链表实现,链表无容量限制,允许元素为null,线程不安全
5. 适合做随机的增加或删除
1. 线程安全
2. 并行性能慢,不建议使用
vector的线程的安全性是通过加锁的形式来实现的,但由于加的锁范围太大,造成了空间和性能的浪费,所以一般用的少;

1.写时复制
2. 线程安全
3. 适合于读多,写少的场景
4. 写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array
5. 比Vector性能高
6.最终一致性
7.实现了List接口,使用方式与ArrayList类似
缺点:要等数据修改完了之后才能读取到,在修改数据时别人无法读取新数据,但值得一提的是在在复制和替换的时候这个时间是非常短的,所以我们也可以放心大胆使用。
总结:list集合中用的最多的是array list,但如果作为类成员变量来使用,就会在高并发下可能产生线程问题,但作为方法变量就不会存在这个问题;除了我们的array list还有LinkedList,Vector,CopyOnWriteArrayList,这些都是常用的list还需要我们好好学习!!!