一、集合
:::tip
- 单列集合:其中的元素都是单个的;
- Collection 接口有两个重要的子接口 List、Set,他们实现子类都是单列集合
- 双列集合:其中的元素是以键值对的形式出现的;
- Map 接口实现的子类 是双列集合
:::
1. Collection 接口

:::tip
Collection 接口实现类的特点:
- Collection 实现子类可以存放多个元素,每个元素可以是 object;
- 有些实现类可以存放重复的元素,有些不可以;
- 有些实现类是有序的(List),有些是无序的(Set);
- Collection 接口没收直接实现子类,它是通过子接口 List 和 set 来实现的;
:::
Collection 接口常用方法
add | 添加单个元素 |
---|
remove | 删除指定元素 |
contains | 查找元素是否存在 |
size | 获取元素个数 |
isEmpty | 判断是否为空 |
clear | 清空 |
addAll | 添加多个元素 |
contains | 查找多个元素是否都存在 |
removeAll | 删除多个元素 |
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
| public class Collection01 { @SuppressWarnings({"all"}) public static void main(String[] args) { List list = new ArrayList(); list.add("西游记"); list.add("水浒传"); System.out.println(list);
List list1 = new ArrayList(); list1.add("射雕英雄传"); list1.add("神雕侠侣"); list.addAll(list1); System.out.println(list);
System.out.println(list.contains("三国演义"));
System.out.println(list.containsAll(list1));
System.out.println(list.isEmpty());
System.out.println(list.size());
list.remove("神雕侠侣"); System.out.println(list);
list.removeAll(list1); System.out.println(list);
list.clear(); System.out.println(list); } }
|
Collection 遍历元素的方式
1. 使用 Iterator (迭代器)
:::tip
- Iterator 对象成为迭代器,主要用于遍历 Collection 集合中的元素;
- 所有实现 Collection 接口的集合类都有一个 iteration 方法;
- 用以返回一个实现了 iteration 接口的对象,即迭代器
- Iterator 仅用于遍历集合,其本身并不存放对象;
- 使用快捷方式 itit, 快速创建遍历循环
:::
:::danger
得到集合的迭代器 :Iterator iterator = 集合.iterator();
hasNext() : 判断是否还有下一个元素
nest() :下移,将移动后集合位置上的元素返回
如果需要再次遍历,需要 重置迭代器(即,重新生成迭代器并赋值给之前的变量)
:::
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
| public class Iterator01 { @SuppressWarnings({"all"}) public static void main(String[] args) { ArrayList list = new ArrayList(); list.add(new Book("西游记","罗贯中",100)); list.add(new Book("红楼梦","不知道",130)); list.add(new Book("水浒传","施耐庵",99));
Iterator iterator = list.iterator();
while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println(obj); iterator = list.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println(obj);
} } }
class Book{ private String name; private String author; private double price;
public Book(String name, String author, double price) { this.name = name; this.author = author; this.price = price; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
@Override public String toString() { return name + author + price + ""; } }
|
2. 使用增强 for 循环迭代
增强 for 循环是简化版的 iterator,只能用于遍历集合或数组;
其本质底层还是使用的迭代器;
1 2 3
| for(元素类型 元素名:集合名/数组名){ 访问元素 }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Iterator01 { @SuppressWarnings({"all"}) public static void main(String[] args) { ArrayList list = new ArrayList(); list.add(new Book("西游记","罗贯中",100)); list.add(new Book("红楼梦","不知道",130)); list.add(new Book("水浒传","施耐庵",99)); for (Object obj:list) { System.out.println(obj); } } }
|
1. List 接口
:::tip
List 接口是 Collection 接口的子接口
- List 集合类中的元素是有序的,且可以重复;
- 元素顺序就是添加时的顺序;
- 如: [tom, jack, menar, jack]
- List 集合类中的每个元素都有其对应的 索引;
- List 容器中的元素可以根据序号存取
:::
list 接口的常用方法
void add(int index, Object ele): | 在 index 位置插入 ele 元素 |
---|
boolean addAll(int index, Collection eles): | 从 index 位置开始将 eles 中的所有元素添加进来 |
Object get(int index): | 获取 index 位置的元素 |
int indexOf(Object obj): | 返回 obj 在集合中首次出现的位置 |
int lastIndexOf(Object obj): | 返回 obj 在集合中 末次出现的位置 |
Object remove(int index): | 移除 index 位置的元素,并返回此元素 |
Object set(int index,Object ele): | 对 index 位置的元素 重新赋值 |
List subList(int formIndex, int toIndex): | 返回 从 fromIndex 到 toIndex 位置的子集合 |
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
| public class List01 { @SuppressWarnings({"ALL"}) public static void main(String[] args) { List list = new ArrayList(); list.add(0,"钢铁侠"); list.add(1,"蜘蛛侠"); list.add(2,"绿巨人"); System.out.println(list);
List list1 = new ArrayList(); list1.add(0,"黑寡妇"); list1.add(1,"美国队长"); list1.add(2,"黑豹"); list1.add(3,"蜘蛛侠"); list.addAll(3,list1); System.out.println(list1); System.out.println(list.get(4));
System.out.println(list.indexOf("蜘蛛侠"));
System.out.println(list.lastIndexOf("蜘蛛侠"));
System.out.println(list.remove(6)); System.out.println(list.remove("黑寡妇"));
list.set(2,"版纳博士"); System.out.println(list);
System.out.println(list.subList(3,list.size())); } }
[钢铁侠, 蜘蛛侠, 绿巨人] [黑寡妇, 美国队长, 黑豹, 蜘蛛侠] 美国队长 1 6 蜘蛛侠 true [钢铁侠, 蜘蛛侠, 版纳博士, 美国队长, 黑豹] [美国队长, 黑豹]
|
List [ArrayList, LinkedList, Vector]的三种遍历方式:
- 使用 iterator
- 加强 for 循环
- 使用普通的 for 循环
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 class List03 { @SuppressWarnings({"all"}) public static void main(String[] args) { ArrayList arrayList = new ArrayList(); arrayList.add(0,new Book("西游记","吴承恩",200)); arrayList.add(1,new Book("红楼梦","曹雪芹",150)); arrayList.add(2,new Book("三国志","罗贯中",220)); System.out.println(arrayList); sort(arrayList); for (Object o:arrayList){ System.out.println(o); } } public static void sort(ArrayList list){ int size = list.size(); for (int i = 0; i < size - 1; i++) { for (int j = 0; j < size - 1 - i; j++) { Book book1 = (Book) (list.get(j)); Book book2 = (Book) (list.get(j + 1)); if (book1.getPrice() < book2.getPrice()){ list.set(j, book2); list.set(j+1, book1); } } } } }
class Book{ private String name; private String author; private double price; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public Book(String name, String author, double price) { this.name = name; this.author = author; this.price = price; } @Override public String toString() { return String.format("名称: %s\t\t 价格: %.2f\t\t 作者: %s",name,price,author); } }
|

ArrayList
:::danger
ArrayList 注意事项:
- ArrayList 可以存放任何元素,包括 null ,不限制个数;
- ArrayList 是由数组来实现数据存储的;
- ArrayList 基本等同于 Vector,执行效率高;
- ArrayList 是线程不安全的(没有 synchronized 修饰),多线程时,不建议使用;
- synchronized 表示线程互斥,起到线程安全的作用
:::
:::warning
ArrayList 底层源码分析:
- ArrayList 中维护了一个 Object 类型的数组 elementData
- transient Object[] elementData;
- 当创建 ArrayList 对象时,如果使用的是无参构造器,则初始 elementData 容量为 0;
- 第一次添加,则扩容 elementData 为 10;
- 如需再次扩容,则扩容 elementData 为 1.5 倍;
- 如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小;
- 如果需要扩容,则直接扩容 elementData 为 1.5 倍
:::
Vector
Vector 和 ArrayList 比较:
| 底层结构 | 版本 | 线程同步 | 扩容倍数 |
---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 有参构造默认为 15,以后以 1.5 倍扩容;无参构造,第一次为 10,以后以 1.5 倍扩 |
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 无参构造,默认为 10,以后以 2 倍扩容;如果指定大小,以后以 2 倍扩容; |
:::warning
Vector 底层源码分析:
- Vector 底层也是一个对象数组;
- protected Object[] elementD
- Vector 是线程同步的,即线程安全,操作方法带有 synchronized
- 在开发中,需要使用线程同步安全时,使用 Vector
- 当创建 Vector 对象时,如果使用的是无参构造器,则初始 elementData 容量为 0;
- 第一次添加,则扩容 elementData 为 10;
- 如需再次扩容,则扩容 elementData 为 2 倍;
- 如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小;
- 如果需要扩容,则直接扩容 elementData 为 2 倍
:::
LinkedList
LinkedList 和 ArrayList 比较:
| 底层结构 | 增删效率 | 改查效率 | 线程安全 |
---|
ArrayList | 可变数组 | 较低,数组扩容 | 较高 | 不安全 |
LinkedList | 双向链表 | 较高,通过链表追加 | 较低 | 不安全 |
- 如果我们改查的操作多,选择 ArrayList;
- 如果我们增删的操作多,选择 LinkedList;
- 大部分情况下都是查询操作,所以一般会选择 ArrayList;
- 也可以一个模块使用 ArrayList,一个模块使用 LinkedList;
:::warning
LinkedList 底层机制:
- LinkedList 底层维护了一个双向链表;
- LinkedList 中维护了两个属性:
- first 和 last 分别指向 首节点 和 尾节点;
- 每个节点(Node 对象)里面又维护了 prev、next、item 三个属性,最终实现双向链表;
- 通过 prev 指向前一个;
- 通过 next 指向后一个;

- LinkedList 的元素增删 不是用过数组完成的,所以效率较高;
- 可以添加任意元素(元素可以重复),包括 null;
- 线程不安全,没有实现同步;
:::
2. Set 接口
:::tip
- 无序(添加和去除的顺序不一致),没有索引;
- 不允许重复元素,所有最多包含一个 null;
- 可以使用迭代器 和 增强for 循环进行遍历;
:::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Set01 { @SuppressWarnings({"all"}) public static void main(String[] args) { HashSet hashSet = new HashSet();
for (int i = 0; i < 5; i++) { hashSet.add("set - " + i); } System.out.println(hashSet);
Iterator iterator = hashSet.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); }
hashSet.remove("set - 2"); System.out.println(hashSet); } }
|
HashSet
:::note
- HashSet 实现了 Set 接口,实际上是 HashMap,底层维护的是一个 数组 + 单向链表;
- 只能存放一个 null;
- HashSet 是无序且不重复的;
:::
:::warning
HashSet 底层原理:
- HashSet 底层是 HashMap;
- 添加一个元素时,先得到该元素的 hash 值,然后转换为 索引值;
- 找到存储数据表 table,查看索引位置是否存在元素;
- 如果不存在,则直接放入;
- 如果存在,则调用 equals 方法进行比较,如果相同,就放弃添加,如果不同,则添加到最后;
- 如果一条链表的元素个数超过 TREEIFY_THRESHOLD(默认为 8),并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认为 64),就会进行树化(红黑树);
:::

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
| public class HashSet01 { @SuppressWarnings({"all"}) public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Employee("老王",18)); hs.add(new Employee("张三",21)); hs.add(new Employee("李四",13)); hs.add(new Employee("老王",18)); System.out.println(hs);
} }
class Employee{ private String name; private int age;
public Employee(String name, int age) { this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return age == employee.age && Objects.equals(name, employee.name); }
@Override public int hashCode() { return Objects.hash(name, age); }
@Override public String toString() { return "Employee{" + name + '\'' + age +'}'; } }
[Employee{张三'21}, Employee{老王'18}, Employee{李四'13}]
|
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
| package com.jihe.set_;
import java.util.HashSet; import java.util.Iterator; import java.util.Objects;
public class HashSet02 { @SuppressWarnings({"all"}) public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Employee1("张三",10000,new MyDate(1980,2,2))); hs.add(new Employee1("李四",12000,new MyDate(1982,4,7))); hs.add(new Employee1("王五",16000,new MyDate(1979,12,2))); hs.add(new Employee1("张三",18000,new MyDate(1980,2,2)));
Iterator iterator = hs.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); } } }
class Employee1{ private String name; private double asl; private MyDate birthday;
public Employee1(String name, double asl, MyDate birthday) { this.name = name; this.asl = asl; this.birthday = birthday; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getAsl() { return asl; }
public void setAsl(double asl) { this.asl = asl; }
public MyDate getBirthday() { return birthday; }
public void setBirthday(MyDate birthday) { this.birthday = birthday; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee1 employee1 = (Employee1) o; return Objects.equals(name, employee1.name) && Objects.equals(birthday, employee1.birthday); }
@Override public int hashCode() { return Objects.hash(name, birthday); }
@Override public String toString() { return "Employee1{" + "name='" + name + '\'' + ", asl=" + asl + ", birthday=" + birthday + '}'; } }
class MyDate{ private int year; private int month; private int day;
public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; }
public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
public int getMonth() { return month; }
public void setMonth(int month) { this.month = month; }
public int getDay() { return day; }
public void setDay(int day) { this.day = day; }
@Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyDate myDate = (MyDate) o; return year == myDate.year && month == myDate.month && day == myDate.day; }
@Override public int hashCode() { return Objects.hash(year, month, day); } }
|
LinkedHashSet
:::note
- LinkedHashSet 是 HashSet 的子类;
- LinkedHashSet 底层是一个 LinkedHashMap,维护了一个 数组 + 双向链表;
- LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置;
- 使用链表维护元素的次序,这使的元素看起来是以插入顺序保存的;
- LinkedHashSet 不允许添加重复元素;
:::
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
| public class LinkedHashSet02 { @SuppressWarnings({"all"}) public static void main(String[] args) { Set lhs = new LinkedHashSet(); lhs.add(new Car("保时捷",900000)); lhs.add(new Car("纳智捷",100000)); lhs.add(new Car("保时捷",900000)); lhs.add(new Car("奥迪",666666));
Iterator it = lhs.iterator(); while (it.hasNext()) { Object o = it.next(); System.out.println(o); } }
}
class Car{ private String name; private double price;
public Car(String name, double price) { this.name = name; this.price = price; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Car car = (Car) o; return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name); }
@Override public int hashCode() { return Objects.hash(name, price); }
@Override public String toString() { return "Car{" + "name='" + name + '\'' + ", price=" + price + '}'; } }
|
TreeSet
:::tip
- 使用 TreeSet 的无参构造器创建的对象是无序的;
- 使用 TreeSet 提供的有参构造器,传入一个比较器(匿名内部类)并必定规则;
:::
1 2 3 4 5 6 7 8 9
|
TreeSet treeSet = new TreeSet(new Comparator(){ @Override public int compare(Object o1, Object o2){ return ((String)o2).compareTo((String) o1); }; });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class TreeSet1 { @SuppressWarnings({"all"}) public static void main(String[] args) { TreeSet t = new TreeSet(new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String)o2).compareTo((String) o1); } }); t.add("a1"); t.add("b3"); t.add("d6"); t.add("c4");
System.out.println(t); } }
[d6, c4, b3, a1]
|
2. Map 接口

:::tip
Map 接口的特点:
- Map 和 Collection 并列存在,用于保存具有映射关系的数据: Key - Value;
- Map 中的 key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中;
- Map 中的 key 不允许重复,value 可以重复;
- Map 的 key 可以为 null,value 也可以为 null;
- 注意: key 为 null 的只能有一个,value 为 null 可以有多个;
- 常用 String 类型为 Map 的 key;
- key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value;
- 一堆 key - value 是放在一个 HashMap$Node 中的,因为 Node 实现了 Entry 接口,所以也说 一对 k-v 就是一个 Entry;
- 通过 keySet 和 valuesSet 方法,可以获取所有 key 或 value 的集合;

:::
Map 接口的常用方法
put | 添加 | 如果key 存在,则更新值 |
---|
remove | 删除 | 如果 key 不存在,则返回 null;如果 key 存在,则返回 value |
get | 根据键获取值 | 返回 value |
size | 获取元素个数 | 返回元素个数 |
isEmpty | 判断个数是否为 0 | 返回 布尔值 |
clear | 清空 | |
containsKey | 查找键是否存在 | 返回布尔值 |
keySet | 获取所有的键 | |
values | 获取所有的值 | |
entrySet | 获取所有关系 k-v |
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
| public class Map01 { @SuppressWarnings({"all"}) public static void main(String[] args) { HashMap hm = new HashMap(); System.out.println(hm.isEmpty());
hm.put("张三",18); hm.put("李四",20); hm.put("王五",30); hm.put("赵六",18); System.out.println(hm);
hm.put("张三",99); System.out.println(hm);
System.out.println(hm.remove("王五"));
System.out.println(hm.get("张三"));
System.out.println(hm.containsKey("123")); System.out.println(hm.containsKey("李四"));
System.out.println(hm.keySet()); System.out.println(hm.values()); Set entrySet = hm.entrySet(); for (Object entry : entrySet) { Map.Entry m = (Map.Entry) entry; System.out.println(m.getKey() + "-" + m.getValue()); }
hm.clear(); System.out.println(hm); } }
true {李四=20, 张三=18, 王五=30, 赵六=18} {李四=20, 张三=99, 王五=30, 赵六=18} 30 99 false true [李四, 张三, 赵六] [20, 99, 18] 李四-20 张三-99 赵六-18 {}
|
1. HashMap
:::note
HashMap :
- Map 接口的常用实现类: HashMap、Hashtable 和 Properties;
- HashMap 是 Map 接口使用频率最高的实现类;
- HashMap 是以 key-value 对的方式来存储数据;
- key 不能重复,但是值可以重复,允许有一个key 为 null 的元素;
- 如果添加相同的 key,则会覆盖原来的 key-value,等同于修改;
- HashMap 没有实现同步,因此是线程不安全的;
:::
:::warning
HashMap 底层机制:
- HashMap 与 HashSet 的扩容机制相同
- HashMap 底层维护了 Node 类型的数组 table,默认为 null;
- 当创建对象时,将加载因子初始化为 0.75;
- 当添加 key-value 时,通过 key 的哈希值得到在 table 的索引,然后判断该索引是否有元素;
- 如果该索引处有元素,继续判断该元素的 key 是否和准备加入的可以相等;
- 如果相等,则直接替换 value
- 如果不相等,则需要判断是树结构还是链表结构,做出相应的处理;
- 如果添加时发现容量不够,则需要扩容;
- 第一次添加,则需要扩容 table 容量为 16,临界值为 12;
- 以后扩容,则需要扩容 table 容量为原来的 2 倍,临界值为原来的 2 倍;
- 如果一条链表元素个数超过 8,并且 table 大小 大于等于 64,则会进行树化;
:::
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
| public class Map02 { @SuppressWarnings({"all"}) public static void main(String[] args) { HashMap hashMap = new HashMap(); hashMap.put("001",new Person("001","张三",20000)); hashMap.put("002",new Person("002","李四",12000)); hashMap.put("003",new Person("003","王五",26000)); hashMap.put("004",new Person("004","赵六",18000));
Set keySet = hashMap.keySet(); for (Object value :keySet) { Person p = (Person) hashMap.get(value); if (p.getSal() > 18000){ System.out.println(p); } } } }
class Person{ private String name,id; private double sal;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public double getSal() { return sal; }
public void setSal(double sal) { this.sal = sal; }
public Person(String id,String name, double sal) { this.name = name; this.id = id; this.sal = sal; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", id='" + id + '\'' + ", sal=" + sal + '}'; } }
|
2.HashTable
:::tip
- HashTable 存放元素是键值对:即 key-value;
- HashTable 的键和值不能为 null,否则会抛出 空指针异常(NullPointerException)
- HashTable 的使用方法和 HashMap 基本一致;
- HashTable 的线程是安全的,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
| public class HashTable01 { @SuppressWarnings({"all"}) public static void main(String[] args) { Hashtable hashtable = new Hashtable();
hashtable.put("001","张三"); hashtable.put("002","李四"); hashtable.put("003","王五"); System.out.println(hashtable);
hashtable.put("001","令狐冲"); System.out.println(hashtable);
hashtable.remove("003"); System.out.println(hashtable);
hashtable.put(null,"test"); } }
|
3. Properties
:::tip
- Properties 类继承自 HashTable 类,并实现了 Map 接口;
- Properties 是以键值对的形式存储,不能使用 null;
- Properties 与 HashTable 类似;
- Properties 可用于从 xxx.properties 文件中,加载数据到 Properties 类对象,并进行修改和读取;
- xxx.properties 通常作为配置文件;
:::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Properties01 { public static void main(String[] args) { Properties p = new Properties();
p.put("001","张三疯"); p.put("002","张三丰"); p.put("003","张君宝"); System.out.println(p);
p.put("001","张无忌"); System.out.println(p);
System.out.println(p.get("002"));
p.remove("003"); System.out.println(p); } }
|
4. TreeMap
TreeSet 和 TreeMap 的区别:
- 底层数据不同
- TreeSet 底层的 key 值是传入的值,value 是一个固定值;
- TreeMap 底层的 key 和 value 都是可变的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class TreeMap1 { @SuppressWarnings({"all"}) public static void main(String[] args) { TreeMap t = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String)o2).compareTo((String) o1); } }); t.put("a1","张三"); t.put("b2","李四"); t.put("d4","王五"); t.put("c3","赵六");
System.out.println(t); } }
{d4=王五, c3=赵六, b2=李四, a1=张三}
|
3. 如何选择集合实现类
:::danger
- 判断存储的类型(一组对象或一组键值对);
- 一组对象:Collection 接口
- 允许重复:List
- 增删多:LinkedLiist(底层维护了一个双向链表);
- 改查多:ArrayList(底层维护 Object 类型的可变数组);
- 不允许重复:Set
- 无序:HashSet(底层是 HashMap,维护了一个哈希表,即(数组+链表+));
- 有序:TreeSet
- 插入和取出的顺序一致:LinkedHashSet(底层维护了一个数组+双向链表);
- 一组键值对:Map 接口
- 键无序:HashMap(底层是:哈希表,数组+链表+红黑树);
- 键有序:TreeMap
- 键插入和取出顺序一致:LinkedHashMap
- 读取文件:Properties
:::
4. Collections 工具类
:::tip
Collections 工具类介绍:
- Collections 是一个操作Set、List 和 Map 等集合的工具类;
- Collections 中提供了一系列静态的方法,对集合元素进行排序、查询、修改等操作;
- 排序操作均为 static 方法;
- reverse(List):反转 List 中元素的顺序;
- shuffle(List): 对 List 集合元素进行随机排序;
- sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
- sort(List,Comparator):根据指定的 Comparator 产生顺序排序;
- swap(List,int,int):将指定 list 集合中的 i 处元素和 j 处元素进行交换;
- 查找、替换
- Object max(Collection):根据自然排序, 返回给定集合中的最大元素;
- Object max(Collection, Comparator): 根据 Comparator 指定条件顺序,返回最大值;
- Object min(Collection):返回最小值;
- Object min(Collection,Comparator): 根据规则返回最小值;
- int frequency(Collection, Object): 返回指定集合中指定元素的出现次数;
- void copy(List dest,List src): 将 src 中的内容复制到 dest 中,新的集合元素个数需要和旧的集合元素个数一致,否则报错;
- boolean replaceAll(List list,Object oldVal, Object newVal): 使用新值替换 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
| public class Collections1 { public static void main(String[] args) { ArrayList al = new ArrayList(); al.add("张三"); al.add("张三疯"); al.add("张山峰"); al.add("张三芬"); System.out.println("原始数组= "+al);
Collections.reverse(al); System.out.println("反转排序= "+al);
Collections.shuffle(al); System.out.println("随机排序= " + al);
Collections.sort(al); System.out.println("根据元素自然排序= "+ al);
Collections.sort(al, new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String)o2).compareTo((String) o1); } }); System.out.println("自定义排序(从大到小)= "+ al);
Collections.swap(al,1,2); System.out.println("下标 1 和 2 互换位置" + al); } }
|

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
| public class Collections1 { @SuppressWarnings({"all"}) public static void main(String[] args) { ArrayList al = new ArrayList(); al.add("张三"); al.add("欧阳疯子"); al.add("鸡儿拖洛夫斯基"); al.add("张三芬"); al.add("张三"); System.out.println("原始数组= "+al);
System.out.println("最大值= " + Collections.max(al));
Object maxs = Collections.max(al, new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String)o1).length() - ((String)o2).length(); } }); System.out.println("最大值= "+ maxs);
System.out.println("元素出现的次数= "+ Collections.frequency(al,"张三"));
ArrayList al1 = new ArrayList(); for(int i = 0; i< al.size(); i++){ al1.add(null); } Collections.copy(al1, al); System.out.println(al1);
Collections.replaceAll(al, "张三", "张无忌"); System.out.println(al); } }
|
案例 1
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
| public class HomeWork1 { @SuppressWarnings({"all"}) public static void main(String[] args) { ArrayList al = new ArrayList(); al.add(new News("新冠确诊病例超千万,数百万印度教信徒赶赴恒河\"圣浴\"引民众担忧")); al.add(new News("男子突然想起 2 月前钓的鱼还在网兜里,捞起一看赶紧放生"));
Collections.reverse(al); Iterator iterator = al.iterator(); while (iterator.hasNext()) { Object o = iterator.next(); News news = (News) o; if (news.getTitle().length() > 15){ String title = news.getTitle().substring(0,15) + "..."; news.setTitle(title); } System.out.println(o); }
} }
class News { private String title, body;
public News(String title) { this.title = title; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
@Override public String toString() { return title; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; } }
男子突然想起 2 月前钓的鱼还... 新冠确诊病例超千万,数百万印度...
|
案例 2
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
| public class HomeWork2 { @SuppressWarnings({"all"}) public static void main(String[] args) { HashMap hm = new HashMap(); hm.put("jack",650); hm.put("tom",1200); hm.put("smith",2900); System.out.println(hm);
hm.put("jack",2600); System.out.println("修改 jack 的工资= " + hm);
for (Object o : hm.keySet()) { hm.put(o,(int)hm.get(o) + 100); } System.out.println("全体加薪 100=" + hm);
Iterator i = hm.keySet().iterator(); while (i.hasNext()) { Object o = (String) i.next(); System.out.println(o); }
for (Object o :hm.values()) { System.out.println(o); } } }
{tom=1200, smith=2900, jack=650} 修改 jack 的工资= {tom=1200, smith=2900, jack=2600} 全体加薪 100={tom=1300, smith=3000, jack=2700} tom smith jack 1300 3000 2700
|
二、泛型
1 2
| interface 接口<T> class 类<K,V>{}
|
:::tip
说明:
- 其中,T、K、V 不代表值,而是表示类型;
- 任何字母都可以,常用 T 或 E 表示;
:::
:::note
- 泛型又称参数化类型,解决数据类型安全性问题;
- 在类声明或实例化时,只要指定号序号的具体类型即可;
- 如:ArrayListdog = new ArraryList;
- 泛型可以保证编译时没有警告,运行时不会抛异常;
- 泛型的作用:
- 可以在类声明时通过一个标识(如:E)表示类中某个属性的类型,或是某个返回值的类型,或者是参数类型
:::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Generic01 { public static void main(String[] args) { Person<String> per = new Person<>("1"); per.f(); } }
class Person<E>{ E s;
public Person(E s){ this.s = s; }
public E f(){ return s; } }
|
:::danger
注意事项:
- 泛型 只能是 引用数据类型,不能是基本数据类型;
- 如: List- 引用数据是类型;
- 如: List会报错 - 基本数据类型;
- 在给泛型指定具体类型后,可以传入该类型或其子类型;
- 如果不指定泛型的类型,默认给他的泛型为 Object;
:::
案例
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
| public class Generic03 { public static void main(String[] args) { ArrayList<Employee> e = new ArrayList<>(); e.add(new Employee("Jack",20000,new MyDate(2,22,1999))); e.add(new Employee("Jack",20000,new MyDate(1,22,1999))); e.add(new Employee("Tom",18000,new MyDate(9,9,2003))); e.add(new Employee("Rose",12000,new MyDate(12,2,1988))); e.add(new Employee("Jack",20000,new MyDate(1,21,1999))); e.add(new Employee("Jack",20000,new MyDate(1,22,1979)));
for (Object o :e) { System.out.println(o); }
e.sort(new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { int res = (o1.getName()).compareTo(o2.getName()); if (res != 0){ return res; } return o1.getBirthday().compareTo(o2.getBirthday()); } }); System.out.println("=========排序后========="); for (Object o :e) { System.out.println(o); }
} }
class Employee{ private String name; private double sal; private MyDate birthday;
public Employee(String name, double sal, MyDate birthday) { this.name = name; this.sal = sal; this.birthday = birthday; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getSal() { return sal; }
public void setSal(double sal) { this.sal = sal; }
public MyDate getBirthday() { return birthday; }
public void setBirthday(MyDate birthday) { this.birthday = birthday; }
@Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", sal=" + sal + ", birthday=" + birthday + '}'; } }
class MyDate implements Comparable<MyDate>{ private int month,day,year;
public MyDate(int month, int day, int year) { this.month = month; this.day = day; this.year = year; }
@Override public int compareTo(MyDate o) { int resYear = year - o.getYear(); if (resYear != 0){ return resYear; }
int resMonth = month - o.getMonth(); if (resMonth != 0){ return resMonth; }
return day - o.getDay(); }
public int getMonth() { return month; }
public void setMonth(int month) { this.month = month; }
public int getDay() { return day; }
public void setDay(int day) { this.day = day; }
public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
@Override public String toString() { return "MyDate{" + "month=" + month + ", day=" + day + ", year=" + year + '}'; } }
|

1. 自定义泛型 - 类
1 2 3 4
| class 类名 <T,R...>{ 成员 }
|
::: warning
注意事项:
- 普通成员可以使用泛型(属性,方法);
- 使用泛型的数组,不能初始化;
- 静态方法中不能使用类的泛型;
- 泛型类的类型,是在创建对象时确定的 ( 创建对象时,需要指定确定的类型 );
- 如果在创建对象时, 没有指定类型, 默认为 Object;
:::
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 class Generic01 { public static void main(String[] args) { Test<Double, String, Integer> test = new Test<>(); test.setD(3.1); test.setS("老王"); } }
class Test<D,S,I>{ private D d; private S s; private I i;
public D getD() { return d; }
public void setD(D d) { this.d = d; }
public S getS() { return s; }
public void setS(S s) { this.s = s; }
public I getI() { return i; }
public void setI(I i) { this.i = i; } }
|
2. 自定义泛型 - 接口
1 2 3 4
| interface 接口名 <T,R...>{ 成员 }
|
:::warning
注意事项:
- 接口中, 静态成员也不能使用泛型;
- 泛型接口的类型, 在继承接口 或者实现接口时确定;
- 没有指定类型, 默认为 Object;
:::
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
| interface IUsb<U, R> { R get(U u); void hi(R r);
void run(R r1, R r2, U u1,U u2);
default R method(U u) { return null; } }
interface Ib extends IUsb<String, Double>{
}
class Fa implements IUsb<Integer,Float>{ @Override public Float get(Integer integer) { return null; }
@Override public void hi(Float aFloat) {
}
@Override public void run(Float r1, Float r2, Integer u1, Integer u2) {
} }
|
3. 自定义泛型 - 方法
1
| 修饰符<T,R...> 返回类型 方法名(参数列表){}
|
:::warning
注意事项:
- 泛型方法, 可以定义在普通方法中, 也可以定义在泛型类中;
- 当泛型方法被调用时, 类型需要确定;
- public void eat(E e){}; , 修饰符后没有表示不是泛型方法,而是使用了泛型;
- 泛型方法, 既可以使用类 声明的泛型, 也可以使用 自己申明的泛型;
:::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class MethodGeneric { public static void main(String[] args) { Car car = new Car(); car.eat("老王", 18); } }
class Car{ public void run(){};
public <T,R> void eat(T t, R r){} }
class Fish<E>{ public void eat(E e){}
public<X> void cat(X x){} }
|
4. 泛型的继承和通配符
- 泛型不具备继承性;
- 如 List
- <?> : 表示支持任意泛型类型;
- <? extends A> : 表示支持 A 类 以及 A 类的子类, 规定了 泛型的上限;
- <? super A> : 表示支持 A 类 以及 A 类的父类, 规定了泛型的 下限;
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
| public class GenericExtends01 { public static void main(String[] args) { List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); List<AA> list3 = new ArrayList<>(); List<BB> list4 = new ArrayList<>(); List<CC> list5 = new ArrayList<>();
collection1(list1); collection1(list2); collection1(list3); collection1(list4); collection1(list5);
collection2(list1); collection2(list2); collection3(list3); collection2(list4); collection2(list5);
collection3(list1); collection3(list2); collection3(list3); collection3(list4); collection3(list5); }
public static void collection1(List<?> list){}; public static void collection2(List<? extends AA> list){}; public static void collection3(List<? super AA> list){}; }
class AA{}
class BB extends AA{}
class CC extends BB{}
|
5. JUnit (单元测试框架)
:::tip
介绍:
- JUnit 是一个 java 语言的单元测试框架;
- 多数 Java 开发环境都已经集成了 JUnit作为单元测试的工具;
- 在需要测试的方法前加上 @Test , 引入相应的库即可
:::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class JUnit01 { public static void main(String[] args) {
}
@Test public void m1(){ System.out.println("m1被调用"); }
@Test public void m2(){ System.out.println("m2被调用"); } }
|
案例
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 80 81 82 83 84 85 86 87 88
| public class HomeWork01 { public static void main(String[] args) {
}
@Test public void tests(){ DAO<User> dao = new DAO<>(); dao.save("1001",new User(1001,20,"tom")); dao.save("1002",new User(1002,20,"jack")); dao.save("1003",new User(1003,20,"wear")); dao.save("1004",new User(1004,20,"sam"));
System.out.println(dao.list()); }
}
class DAO<T>{ Map<String,T> map = new HashMap<>();
public void save(String id, T entity){ map.put(id, entity); }
public T get(String id){ return map.get(id); }
public void update(String id, T entity){ map.put(id, entity); }
public List<T> list(){ List<T> list = new ArrayList<>(); for (String key : map.keySet()) { list.add(get(key)); } return list; } public void delete(String id){ map.remove(id); } }
class User{ private int id,age; private String name;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public User(int id, int age, String name) { this.id = id; this.age = age; this.name = name; }
@Override public String toString() { return "User{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}'; } }
|