目录
【泛化关系】:是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为。例如:老虎是动物的一种,即有老虎的特性也有动物的共性。
【箭头指向】:带三角箭头的实线,箭头指向父类
【实现关系】:是一种类与接口的关系,表示类是接口所有特征和行为的实现.
【箭头指向】:带三角箭头的虚线,箭头指向接口
【关联关系】:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。
【代码体现】:成员变量
【箭头及指向】:带普通箭头的实心线,指向被拥有者
上图中,老师与学生是双向关联,老师有多名学生,学生也可能有多名老师。但学生与某课程间的关系为单向关联,一名学生可能要上多门课程,课程是个抽象的东西他不拥有学生。
下图为自身关联:
【聚合关系】:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。
聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。
【代码体现】:成员变量
【箭头及指向】:带空心菱形的实心线,菱形指向整体
【组合关系】:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。
组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。
【代码体现】:成员变量
【箭头及指向】:带实心菱形的实线,菱形指向整体
【依赖关系】:是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖.
【代码表现】:局部变量、方法的参数或者对静态方法的调用
【箭头及指向】:带箭头的虚线,指向被使用者
各种关系的强弱顺序:
泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖
下面这张UML图,比较形象地展示了各种类图关系:
由于上图map有点不全,可以看下图
特点:元素有序,且可重复
遍历:下标,foreach,迭代器
迭代器:是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合,terator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。
扩容:初始容量10,负载因子0.5,扩容增量0.5倍
新容量 = 原容量 + 原容量 * 0.5 ,
如 ArrayList的容量为10,一次扩容后是容量为15
三种遍历方法
- package com.xyz.test;
-
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class ListTest {
-
- List<Integer> list=new ArrayList<Integer>();
-
- @Before
- public void setup() {
- list.add(1);
- list.add(3);
- list.add(5);
- list.add(2);
- list.add(6);
- list.add(4);
- }
-
-
- /**
- * foreach遍历
- */
- @Test
- public void test01() {
- for (Integer e : list) {
- System.out.println(e);
- }
- }
-
- /**
- * 下标遍历
- */
- @Test
- public void test02() {
- for (int i = 0; i < list.size(); i++) {
- System.out.println(list.get(i));
- }
- }
-
- /**
- * 迭代器遍历
- */
- @Test
- public void test03() {
- Iterator<Integer> it = list.iterator();
- while(it.hasNext()) {
- System.out.println(it.next());
- }
- }
-
- }
简单数据结构,超出容量自动扩容,动态数组
内部实现是基于基础的对象数组的
随机访问快,遍历快
不适合随机增加或删除
适用于不确定数据的最大个数的情况
线程不安全
- package com.xyz.test;
-
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class ListRemove {
-
- private List<Integer> list;
-
- @Before
- public void setup() {
- list=new ArrayList<>();
- list.add(1);
- list.add(2);
- list.add(3);
- list.add(3);
- list.add(4);
- list.add(5);
- }
-
- /**
- * 删除集合中为3的元素的下标
- */
- @Test
- public void test01() {
- for(int i=0;i<list.size();i++){
- if(list.get(i)==3) list.remove(i);
- }
- System.out.println(list);
- //运行结果:[1,2,3,4,5]
- //原因:list.get(i)拿到下标为i的元素,当两个一样的元素3相邻时,
- // 第一个3在判断并删除后,第二个3及其后面的所有元素的下标都会向前移动一位,
- // 这样第二个3就到了第一个3下标所在的位置,但是指针已经判断过第一个3所在位置的元素是否为3了,故不会删除掉第二个3
- }
-
- @Test
- public void test02() {
- for(int i=0;i<list.size();i++){
- if(list.get(i)==3) list.remove(i--);
- }
- System.out.println(list);
- //运行结果:[1,2,4,5]
- //原因:第一个3在判断并删除后,第二个3及其后面的所有元素的下标都会向前移动一位,
- // 使用了i--,即在进行删除之后指针会向前移一位,再回到删除过的下标位置进行判断,
- // 而这样就避免了因为ArrayList的位移现象所导致的判断遗漏。
- }
-
- @Test
- public void test03() {
- for(int i=list.size()-1;i>=0;i--){
- if(list.get(i)==3){
- list.remove(i);
- }
- }
- System.out.println(list);
- //运行结果:[1,2,4,5]
- //原因:这是从集合中的最后一位元素向第一位元素方向进行遍历的倒序遍历方法,
- // 这样即使ArrayList的位移现象发生也无法对删除产生影响
- }
-
- @Test
- public void test04() {
- for(Integer i:list){
- if(i==3) list.remove(i);
- }
- System.out.println(list);
- //运行结果:运行错误
- //原因:因为ArrayList中有一个变量(modCount=原数组的元素个数)还在内部封装了一个内部类(Itr),
- // 这个内部类实现了迭代器,当使用foreach方法遍历时,使用的是ArrayList内部类的迭代器,
- // 其中内部类中定义了一个改变次数的变量(expectedModCount),这个变量被赋值为外部modcount的值,
- // 当使用内部类(Itr)发生增加或者修改操作时,抛出异常,其目的是阻止ArrayList长度发生改变。
- }
-
- @Test
- public void test05() {
- Iterator<Integer> it=list.iterator();
- while(it.hasNext()){
- if(it.next()==3){
- it.remove();
- }
- }
- System.out.println(list);
- //运行结果:[1,2,4,5]
- //ArrayList集合在进行删除、增加等操作时,要考虑其动态位移的特性,推荐使用迭代器,会比较安全
- //上述代码的it.remove不要写成list.remove(i)
- }
-
- @Test
- public void test06() {
- Iterator<Integer> it=list.iterator();
- while(it.hasNext()){
- Integer value=it.next();
- if(value==3){
- list.remove(value);
- }
- }
- System.out.println(list);
- //运行结果:运行错误
- //由于写成了list.remove(value)
- }
-
- @Test
- public void test07() {
- list.remove(2);
- System.out.println(list);
- //运行结果:[1,2,3,4,5]
- //删除下标为2的元素
- }
-
- @Test
- public void test08() {
- list.remove(Integer.valueOf(2));
- System.out.println(list);
- //运行结果:[1,3,3,4,5]
- //删除元素2
- }
-
- }
LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部
线程不安全
LinkedList可被用作堆栈(stack)【包括了push,pop方法】,队列(queue)或双向队列(deque)
以双向链表实现,链表无容量限制,允许元素为null,线程不安全
适合做随机的增加或删除
线程安全
并行性能慢,不建议使用
写时复制
线程安全
适合于读多,写少的场景
写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array
比Vector性能高
最终一致性
实现了List接口,使用方式与ArrayList类似
写时复制是指将原本集合中的数据复制过去,然后在复制过去的集合里的元素进行操作,最终一致性是指修改完成后,这个修改过的集合就会给到原本的集合那里
特点:无序,不重复
遍历:foreach,迭代器
扩容: 初始容量16,负载因子0.75,扩容增量1倍
思考:如何对List容器中的元素去重?试试以下代码吧
- package com.xyz.test;
-
- import java.util.ArrayList;
- import java.util.HashSet;
- import java.util.List;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class SetTest {
-
- private List<Integer> list=new ArrayList<Integer>();
-
- @Before
- public void setup() {
- list.add(1);
- list.add(2);
- list.add(2);
- list.add(3);
- list.add(3);
- list.add(4);
- list.add(5);
- list.add(6);
- }
-
- /**
- * 使用set去重
- */
- @Test
- public void test01() {
- List<Integer> tmp=new ArrayList<Integer>(new HashSet<Integer>(list));
- System.out.println(tmp);
- //运行结果:[1,2,3,4,5,6]
- }
-
- }
- package com.xyz.test;
-
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Set;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class SetTest {
-
- private Set<Integer> set=new HashSet<Integer>();
-
- @Before
- public void setup() {
- set.add(1);
- set.add(1);
- set.add(2);
- set.add(4);
- set.add(4);
- set.add(5);
- set.add(3);
- }
-
- /**
- * foreach遍历
- */
- @Test
- public void test02() {
- for (Integer e : set) {
- System.out.println(e);
- //运行结果:1 2 3 4 5
- }
- }
-
- /**
- * 迭代器遍历
- */
- @Test
- public void test03() {
- Iterator<Integer> it=set.iterator();
- while(it.hasNext()) {
- System.out.println(it.next());
- //运行结果:1 2 3 4 5
- }
- }
-
- }
它存储唯一元素并允许空值,依据对象的hashcode来确定该元素是否存在
由HashMap支持
不保持插入顺序
非线程安全
性能参数:初始容量,负载因子
默认值: 初始容量16,负载因子0.75
示例:new HashSet<>(20, 0.5f);
在上面我们用set对数组进行了去重,那对象我们能不能去重呢?
1.首先我们实例一个学生对象
- package com.xyz.test;
-
- public class Student {
-
- private Integer sid;
- private String sname;
- private Integer age;
-
- public Integer getSid() {
- return sid;
- }
- public void setSid(Integer sid) {
- this.sid = sid;
- }
- public String getSname() {
- return sname;
- }
- public void setSname(String sname) {
- this.sname = sname;
- }
- public Integer getAge() {
- return age;
- }
- public void setAge(Integer age) {
- this.age = age;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((age == null) ? 0 : age.hashCode());
- result = prime * result + ((sid == null) ? 0 : sid.hashCode());
- result = prime * result + ((sname == null) ? 0 : sname.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Student other = (Student) obj;
- if (age == null) {
- if (other.age != null)
- return false;
- } else if (!age.equals(other.age))
- return false;
- if (sid == null) {
- if (other.sid != null)
- return false;
- } else if (!sid.equals(other.sid))
- return false;
- if (sname == null) {
- if (other.sname != null)
- return false;
- } else if (!sname.equals(other.sname))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
- }
-
- public Student(Integer sid, String sname, Integer age) {
- super();
- this.sid = sid;
- this.sname = sname;
- this.age = age;
- }
-
-
-
- }
不要忘了加上hashCode和equals方法,因为这两个方法是用来判断对象是否相同的
2.下面我们来测试一下能不能去重
我们增加两个id,姓名,年龄一样的对象
- package com.xyz.test;
-
- import java.util.HashSet;
- import java.util.Set;
-
- import org.junit.Test;
-
- public class SetTest {
-
- @Test
- public void test04() {
- Set<Student> stu=new HashSet<Student>();
-
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(2, "小黄", 16));
- stu.add(new Student(3, "小白", 24));
- stu.add(new Student(4, "张三", 18));
- stu.add(new Student(5, "李四", 23));
-
- for (Student s : stu) {
- System.out.println(s);
- }
- }
-
- }
结果显示,只有一个增加进去了
那我们来试一下增加两个id不同,姓名年龄相同的,看是都能增加进去还是只能增加一个?
- package com.xyz.test;
-
- import java.util.HashSet;
- import java.util.Set;
-
- import org.junit.Test;
-
- public class SetTest {
-
- @Test
- public void test04() {
- Set<Student> stu=new HashSet<Student>();
-
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(2, "小黑", 18));
- stu.add(new Student(3, "小黄", 16));
- stu.add(new Student(4, "小白", 24));
- stu.add(new Student(5, "张三", 18));
- stu.add(new Student(6, "李四", 23));
-
- for (Student s : stu) {
- System.out.println(s);
- }
- }
-
- }
结果显示:两个都增加成功了,因为id不同,说明这不是同一个人,没有人规定不能同名同龄
如果要进行排序,那么需要用到TreeSet
- 是一个包含有序的且没有重复元素的集合
- 作用是提供有序的Set集合,自然排序或者根据提供的Comparator进行排序
- TreeSet是基于TreeMap实现的
排序有两种方法,第一种是在测试类写一个比较器进行比较 ,如下面代码所示
- package com.xyz.test;
-
- import java.util.Comparator;
- import java.util.Set;
- import java.util.TreeSet;
-
- import org.junit.Test;
-
- public class SetTest {
-
- @Test
- public void test04() {
- Set<Student> stu=new TreeSet<Student>(new Comparator<Student>() {
- @Override
- public int compare(Student o1, Student o2) {
- return o1.getSid()-o2.getSid();
- //o1代表第一个对象,o2代表第二个对象
- //o1-o2就可以实现升序
- //o2-o1就是实现降序
- }
- });
-
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(5, "小黄", 16));
- stu.add(new Student(6, "小白", 24));
- stu.add(new Student(3, "张三", 18));
- stu.add(new Student(4, "李四", 23));
-
- for (Student s : stu) {
- System.out.println(s);
- }
- }
-
- }
可以看到,这个既去重了又排序了
第二种是在实体类中写一个比较器。下面代码所示
- package com.xyz.test;
-
- import java.util.Comparator;
-
- public class Student implements Comparable<Student>{
-
- private Integer sid;
- private String sname;
- private Integer age;
-
- public Integer getSid() {
- return sid;
- }
- public void setSid(Integer sid) {
- this.sid = sid;
- }
- public String getSname() {
- return sname;
- }
- public void setSname(String sname) {
- this.sname = sname;
- }
- public Integer getAge() {
- return age;
- }
- public void setAge(Integer age) {
- this.age = age;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((age == null) ? 0 : age.hashCode());
- result = prime * result + ((sid == null) ? 0 : sid.hashCode());
- result = prime * result + ((sname == null) ? 0 : sname.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Student other = (Student) obj;
- if (age == null) {
- if (other.age != null)
- return false;
- } else if (!age.equals(other.age))
- return false;
- if (sid == null) {
- if (other.sid != null)
- return false;
- } else if (!sid.equals(other.sid))
- return false;
- if (sname == null) {
- if (other.sname != null)
- return false;
- } else if (!sname.equals(other.sname))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
- }
-
- public Student(Integer sid, String sname, Integer age) {
- super();
- this.sid = sid;
- this.sname = sname;
- this.age = age;
- }
-
- @Override
- public int compareTo(Student o) {
- return this.getSid()-o.getSid();
- }
-
-
- }
测试类
- package com.xyz.test;
-
- import java.util.Comparator;
- import java.util.Set;
- import java.util.TreeSet;
-
- import org.junit.Test;
-
- public class SetTest {
-
- @Test
- public void test04() {
- Set<Student> stu=new TreeSet<Student>();
-
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(1, "小黑", 18));
- stu.add(new Student(5, "小黄", 16));
- stu.add(new Student(6, "小白", 24));
- stu.add(new Student(3, "张三", 18));
- stu.add(new Student(4, "李四", 23));
-
- for (Student s : stu) {
- System.out.println(s);
- }
- }
-
- }
特点:
扩容:初始容量16,负载因子0.75,扩容增量1倍
遍历
无序的,线程不安全,最常用,速度快
内部采用数组来存放数据
基本原理——put执行过程
Table数组中的的Node
1.链表结构示意图
2.红黑树结构示意图
流程图中绿色标出的部分为JDK8新增的处理逻辑,目的是在Table[i]中的Node节点数量大于8时,通过红黑树提升查找速度。
获取键的值
- package com.xyz.test;
-
- import java.util.HashMap;
- import java.util.Map;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class MapTest {
-
- private Map<String, String > map=new HashMap<String, String>();
-
- @Before
- public void setup() {
- map.put("1", "小黑");
- map.put("2", "小白");
- map.put("3", "嘿嘿");
- map.put("4", "小蓝");
- }
-
- /**
- * 获取键的值
- */
- @Test
- public void test01() {
- System.out.println(map.get("1"));
- //运行结果:小黑
- }
-
- }
方法一:先获取map集合的键,再用键获取值,再遍历(通过键获取值)
- package com.xyz.test;
-
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class MapTest {
-
- private Map<String, String > map=new HashMap<String, String>();
-
- @Before
- public void setup() {
- map.put("1", "小黑");
- map.put("1", "小黄");
- map.put("2", "小白");
- map.put("3", "嘿嘿");
- map.put("4", "小蓝");
- map.putIfAbsent("1", "aa");
- }
-
- /**
- * 遍历map数组的值
- */
- @Test
- public void test02() {
- Iterator<String> it = map.keySet().iterator();
- while(it.hasNext()) {
- String key = it.next();
- System.out.println(map.get(key));
- }
- }
-
- }
- 由于小黑和小黄的键重复了,所以后面的小黄覆盖了小黑
- putIfAbsent是判断如果有那个键了就不增加,没有则增加
方法二:用Entry直接获取键与值
- package com.xyz.test;
-
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Map.Entry;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class MapTest {
-
- private Map<String, String > map=new HashMap<String, String>();
-
- @Before
- public void setup() {
- map.put("1", "小黑");
- map.put("1", "小黄");
- map.put("2", "小白");
- map.put("3", "嘿嘿");
- map.put("4", "小蓝");
- map.putIfAbsent("1", "aa");
- }
-
- @Test
- public void test03() {
- Iterator<Entry<String, String>> it = map.entrySet().iterator();
- while(it.hasNext()) {
- Entry<String, String> e = it.next();
- System.out.println("key:"+e.getKey()+"--value:"+e.getValue());
- }
- }
-
- }
线程安全,不太常用
线程安全,比HashTable性能高
- 有序的,key值按一定的顺序排序
- 添加或获取元素时性能较HashMap慢
- 因为需求维护内部的红黑树,用于保证key值的顺序
继承HashMap
维护了一个双向链表
LinkedHashMap是有序的,且默认为插入顺序
- package com.xyz.test;
-
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.LinkedHashMap;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
-
- import org.junit.Before;
- import org.junit.Test;
-
- public class MapTest {
-
- Map<String, String> linkedHashMap = new LinkedHashMap<>();
-
- @Test
- public void linkedHashMap() {
- linkedHashMap.put("5", "嘿嘿");
- linkedHashMap.put("4", "喜喜");
- linkedHashMap.put("1", "哈哈");
- linkedHashMap.put("3", "呵呵");
- linkedHashMap.put("3", "嗨嗨");
- linkedHashMap.put("4", "喜喜");
- linkedHashMap.put("1", "哈哈");
- Set<Entry<String, String>> set = linkedHashMap.entrySet();
- Iterator<Entry<String, String>> iterator = set.iterator();
- while(iterator.hasNext()) {
- Entry entry = iterator.next();
- String key = (String) entry.getKey();
- String value = (String) entry.getValue();
- System.out.println("key:" + key + ",value:" + value);
- }
- }
-
- }
可以看到是有序的,是我们的插入顺序
内容有点多,请大家耐心观看,就说到这啦~