集合
集合
1. 集合介绍
概念:对象的容器,存储对象的对象,可代替数组。
特点:容器的工具类,定义了对多个对象进行操作的常用方法。
位置:java.util.*;
Coolection是集合父接口,List和Set属于其子接口。
2. List接口
2.1 ArrayList
- 常用方法
add(E e) 添加元素在集合末尾
add(int index, E element) 指定位置添加/插入元素
clear() 清空集合
contains(Object o) 判断集合中是否包含某个元素 包含则为true 不包含为false
get(int index) 根据下标获取元素
indexOf(Object o) 判断某个元素第一次出现的位置 未找到返回-1
isEmpty() 判断集合长度是否为0 是则为true 不是为false
iterator() 获取集合迭代器 用于遍历集合
remove(int index) 根据下标删除元素
remove(Object o) 根据对象删除元素
set(int index, E element) 修改元素
size() 获取元素个数
import java.util.ArrayList;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 9:45
* List
* ArrayList 常用方法
*
* add(E e) 添加元素在集合末尾
* add(int index, E element) 指定位置添加/插入元素
* clear() 清空集合
* contains(Object o) 判断集合中是否包含某个元素 包含则为true 不包含为false
* get(int index) 根据下标获取元素
* indexOf(Object o) 判断某个元素第一次出现的位置 未找到返回-1
* isEmpty() 判断集合长度是否为0 是则为true 不是为false
* iterator() 获取集合迭代器 用于遍历集合
* remove(int index) 根据下标删除元素
* remove(Object o) 根据对象删除元素
* set(int index, E element) 修改元素
* size() 获取元素个数
*
*/
public class TestArrayListMethod {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add(123);
list1.add(false);
list1.add('A');
list1.add("abc");
list1.add(3.5);
list1.add(3.5F);
list1.add(new Student());
ArrayList<String> list2 = new ArrayList<String>();
System.out.println("----------------添加操作--------------------------");
list2.add("a");
list2.add("b");
list2.add("c");
list2.add("hello");
list2.add("world");
list2.add("666");
list2.add(1, "世界你好");
System.out.println(list2.size());
System.out.println("集合第一个元素:" + list2.get(0));
System.out.println("集合第二个元素:" + list2.get(1));
System.out.println("集合第三个元素:" + list2.get(2));
System.out.println("集合第四个元素:" + list2.get(3));
System.out.println("集合第五个元素:" + list2.get(4));
System.out.println("集合第六个元素:" + list2.get(5));
System.out.println("集合第七个元素:" + list2.get(6));
System.out.println("----------------修改操作--------------------------");
String oldElement = list2.set(0, "123456 abc");
System.out.println("oldElement = " + oldElement);
oldElement = list2.set(5, "窗前明月光");
System.out.println("oldElement = " + oldElement);
System.out.println("------------------打印--------------------------");
System.out.println("集合第一个元素:" + list2.get(0));
System.out.println("集合第二个元素:" + list2.get(1));
System.out.println("集合第三个元素:" + list2.get(2));
System.out.println("集合第四个元素:" + list2.get(3));
System.out.println("集合第五个元素:" + list2.get(4));
System.out.println("集合第六个元素:" + list2.get(5));
System.out.println("集合第七个元素:" + list2.get(6));
System.out.println("-----------------删除操作----------------------");
System.out.println("在集合中删除内容为'999'的字符串元素:" + list2.remove("999"));
System.out.println("根据下标删除元素:" + list2.remove(0));
System.out.println("-----------------判断是否包含元素----------------------");
System.out.println("集合中是否包含 'hello' :" +list2.contains("hello"));
System.out.println("集合中是否包含 'world' :" +list2.contains("world"));
System.out.println("-----------------判断集合是否为空----------------------");
System.out.println(list2.isEmpty());
System.out.println("-----------------清空集合----------------------");
list2.clear();
System.out.println("-----------------判断集合是否为空----------------------");
System.out.println(list2.isEmpty());
System.out.println(list2.size());
}
}
- 遍历方式
ArrayList集合遍历
1.普通for循环遍历
2.迭代器遍历
3.增强for循环遍历
import java.util.ArrayList;
import java.util.Iterator;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 10:27
* ArrayList集合遍历
* 1.普通for循环遍历
* 2.迭代器遍历
* 3.增强for循环遍历
*/
public class TestArrayListForeach {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(11);
list.add(22);
list.add(33);
list.add(44);
list.add(55);
// 普通for循环遍历
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
System.out.println("---------------------------------------");
// 迭代器遍历
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("---------------------------------------");
// 增强for循环遍历
// 增强for循环是JDK1.5引入的新特性 底层实现还是迭代器
// 可以理解为 属于迭代器的简写形式
for(Integer i : list){
System.out.println("i = " + i);
}
}
}
ArrayList因为底层是数组结构 所以三种遍历方式几乎没有效率差异
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 10:49
* ArrayList因为底层是数组结构 所以三种遍历方式几乎没有效率差异
*/
public class TestArrayListForeachTime {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(); // 热插拔的思想
for(int i = 1;i <= 200000;i++){
list.add(i);
}
long begin = System.currentTimeMillis();
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i));
// }
// Iterator<Integer> iterator = list.iterator();
// while(iterator.hasNext()){
// System.out.println(iterator.next());
// }
for (Integer integer : list) {
System.out.println("integer = " + integer);
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
}
- 数据结构和源代码
ArrayList集合特点:有序 允许重复元素 允许元素为null 有下标 线程不安全
数据结构:数组
调用ArrayList无参构造方法,将维护一个长度为0的空数组,当我们第一次添加元素,将数组的长度初始化为10
这里使用了懒加载的思想 Lazy Load 集合扩容为原集合长度的1.5倍
增删改查效率:
查询 修改 效率高
删除 需要移动元素的情况,添加 需要扩容的情况,插入 需要移动元素的情况 效率低
2.2 LinkedList
- 常用方法
List Deque
LinkedList 基于双向链表的集合 除了提供有和ArrayList相同的方法以外 还单独提供了用于操作
头部和尾部的方法
add(E e) 添加元素在集合末尾
add(int index, E element) 指定位置添加/插入元素
clear() 清空集合
contains(Object o) 判断集合中是否包含某个元素 包含则为true 不包含为false
get(int index) 根据下标获取元素
indexOf(Object o) 判断某个元素第一次出现的位置 未找到返回-1
isEmpty() 判断集合长度是否为0 是则为true 不是为false
iterator() 获取集合迭代器 用于遍历集合
remove(int index) 根据下标删除元素
remove(Object o) 根据对象删除元素
set(int index, E element) 修改元素
size() 获取元素个数
addFirst() 添加在链表的头部
addLast() 添加在链表的尾部
removeFirst() 删除头部元素
removeLast() 删除尾部元素
getFirst() 获取头部元素
getLast() 获取尾部元素
import java.util.LinkedList;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 14:05
* List Deque
* LinkedList 基于双向链表的集合 除了提供有和ArrayList相同的方法以外 还单独提供了用于操作
* 头部和尾部的方法
* add(E e) 添加元素在集合末尾
* add(int index, E element) 指定位置添加/插入元素
* clear() 清空集合
* contains(Object o) 判断集合中是否包含某个元素 包含则为true 不包含为false
* get(int index) 根据下标获取元素
* indexOf(Object o) 判断某个元素第一次出现的位置 未找到返回-1
* isEmpty() 判断集合长度是否为0 是则为true 不是为false
* iterator() 获取集合迭代器 用于遍历集合
* remove(int index) 根据下标删除元素
* remove(Object o) 根据对象删除元素
* set(int index, E element) 修改元素
* size() 获取元素个数
*
* addFirst() 添加在链表的头部
* addLast() 添加在链表的尾部
*
* removeFirst() 删除头部元素
* removeLast() 删除尾部元素
*
* getFirst() 获取头部元素
* getLast() 获取尾部元素
*/
public class TestLinkedListMethod {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
linkedList.add("d");
linkedList.add("e");
linkedList.addFirst("世界你好");
linkedList.addLast("hello world");
linkedList.add(3, "6666");
System.out.println("集合的长度/元素个数:" + linkedList.size());
System.out.println("第1个元素:" + linkedList.get(0));
System.out.println("第2个元素:" + linkedList.get(1));
System.out.println("第3个元素:" + linkedList.get(2));
System.out.println("第4个元素:" + linkedList.get(3));
System.out.println("第5个元素:" + linkedList.get(4));
System.out.println("第6个元素:" + linkedList.get(5));
System.out.println("第7个元素:" + linkedList.get(6));
System.out.println("第8个元素:" + linkedList.get(7));
System.out.println("获取第一个元素:" + linkedList.getFirst());
System.out.println("获取最后一个元素:" + linkedList.getLast());
System.out.println("---------------------------------------");
System.out.println("删除第一个元素,返回值为删除的元素:" + linkedList.remove());
System.out.println("删除第一个元素,返回值为删除的元素:" + linkedList.removeFirst());
System.out.println("根据下标删除元素,返回值为删除的元素:" + linkedList.remove(3));
System.out.println("根据对象删除元素,返回值为布尔类型:" + linkedList.remove("a"));
System.out.println("---------------------------------------");
System.out.println("第1个元素:" + linkedList.get(0));
System.out.println("第2个元素:" + linkedList.get(1));
System.out.println("第3个元素:" + linkedList.get(2));
System.out.println("第4个元素:" + linkedList.get(3));
System.out.println("---------------------------------------");
System.out.println("修改下标为1的元素:" + linkedList.set(1, "中国666"));
System.out.println(linkedList.get(1));
System.out.println("---------------------------------------");
System.out.println("判断是否包含元素'6666':" + linkedList.contains("6666"));
linkedList.add("b");
linkedList.add("b");
linkedList.add("b");
System.out.println("查找第一个'b'出现的位置:" + linkedList.indexOf("b"));
System.out.println("查找最后一个'b'出现的位置:" + linkedList.lastIndexOf("b"));
System.out.println("查找最后一个'你好'出现的位置:" + linkedList.lastIndexOf("你好"));
linkedList.clear();
System.out.println("判断集合是否为空/长度是否为0:" + linkedList.isEmpty());
}
}
- 遍历方式
LinkedList三种遍历方式
1.普通for循环
2.迭代器遍历
3.增强for循环
import java.util.Iterator;
import java.util.LinkedList;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 14:21
* LinkedList三种遍历方式
* 1.普通for循环
* 2.迭代器遍历
* 3.增强for循环
*/
public class TestLinkedListForeach {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(11);
list.add(12);
list.add(13);
list.add(14);
list.add(15);
// 1.普通for循环
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
System.out.println("------------------------------------------");
// 2.迭代器遍历
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("------------------------------------------");
// 3.增强for循环
for (Integer integer : list) {
System.out.println("integer = " + integer);
}
}
}
LinkedList使用普通for循环遍历 效率非常低 所以尽可能不使用
import java.util.Iterator;
import java.util.LinkedList;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 14:26
* LinkedList使用普通for循环遍历 效率非常低 所以尽可能不使用
*/
public class TestLinkedListForeachTime {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
for(int i = 0;i < 2000000;i++){
list.add(i);
}
long begin = System.currentTimeMillis();
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i));
// }
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// for (Integer integer : list) {
// System.out.println("integer = " + integer);
// }
long end = System.currentTimeMillis();
System.out.println(end - begin);
}
}
- 数据结构和源代码
LinkedList集合特点:有序 有下标 允许重复元素 允许元素为null 线程不安全
数据结构:双向链表
LinkedList集合中的每一个元素称之为一个Node(节点)
每个节点包含三部分:1.当前元素本身 2.上一个元素的引用 3.下一个元素的引用
增删改查效率:
所有根据下标所执行的操作 效率都低
查询 修改 效率低
删除不使用下标的情况 效率高 因为不涉及元素移动
添加不使用下标的情况 效率高 因为不涉及扩容 复制元素的操作
2.3 Vector
ArrayList和Vector的区别?
ArrayList线程不安全 JDK1.2
Vector线程安全 JDK1.0
ArrayList初始容量为0 扩容1.5倍
Vector初始容量为10 扩容2倍
import java.util.Vector;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 15:38
* ArrayList和Vector的区别?
* ArrayList线程不安全 JDK1.2
* Vector线程安全 JDK1.0
* ArrayList初始容量为0 扩容1.5倍
* Vector初始容量为10 扩容2倍
*/
public class TestVector {
public static void main(String[] args) {
Vector<Character> vector = new Vector<>();
vector.add('A');
vector.add('B');
vector.add('C');
vector.add('D');
vector.add('E');
System.out.println(vector.size());
System.out.println(vector.remove(1));
vector.set(0, 'S');
System.out.println(vector.get(0));
System.out.println(vector.get(1));
System.out.println(vector.get(2));
System.out.println(vector.get(3));
// 遍历方式与ArrayList相同 三种
vector.clear();
System.out.println(vector.isEmpty());
}
}
3.Set接口
3.1 HashSet
HashSet 无序 元素不能重复 可以为null 没有下标 线程不安全 底层实现为HashMap实例
add() 添加元素
remove() 删除元素
clear() 清空集合
isEmpty() 判断集合长度是否为0
iterator() 获取迭代器
contains(Object o) 判断是否包含某个元素
size() 获取元素个数
总结:HashSet中保存的元素 JDK会自动创建HashMap集合 帮我们保存在HashMap集合键的位置
HashSet中的元素/HashMap中的键 去除重复的依据?
两个对象的equals比较为true 并且hashCode相同 则认为是重复的元素
import java.util.HashSet;
import java.util.Iterator;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 15:05
* HashSet 无序 元素不能重复 可以为null 没有下标 线程不安全 底层实现为HashMap实例
* add() 添加元素
* remove() 删除元素
* clear() 清空集合
* isEmpty() 判断集合长度是否为0
* iterator() 获取迭代器
* contains(Object o) 判断是否包含某个元素
* size() 获取元素个数
*
* 总结:HashSet中保存的元素 JDK会自动创建HashMap集合 帮我们保存在HashMap集合键的位置
*
* HashSet中的元素/HashMap中的键 去除重复的依据?
* 两个对象的equals比较为true 并且hashCode相同 则认为是重复的元素
*
*/
public class TestHashSet {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
System.out.println(set.add("a"));
System.out.println(set.add("a"));
set.add("b");
set.add("c");
set.add("d");
set.add("c");
System.out.println("集合长度:" + set.size());
System.out.println("删除元素a:" + set.remove("a"));
System.out.println("是否包含a元素:" + set.contains("a"));
// HashSet两种遍历方式
// 1.迭代器方式
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// 2.增强for循环
for (String s : set) {
System.out.println("s = " + s);
}
set.clear();
System.out.println("集合是否为空:" + set.isEmpty());
}
}
import java.util.HashSet;
import java.util.Objects;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 15:12
*/
public class Student {
private String name;
private int 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
public static void main(String[] args) {
Student stu1 = new Student("赵四1", 29);
Student stu2 = new Student("赵四1", 29);
Student stu3 = new Student("赵四1", 29);
Student stu4 = new Student("赵四1", 29);
HashSet<Student> hashSet = new HashSet<>();
System.out.println(hashSet.add(stu1));
System.out.println(hashSet.add(stu2));
System.out.println(hashSet.add(stu3));
System.out.println(hashSet.add(stu4));
System.out.println(hashSet.size());
}
}
3.2 LinkedHashSet
LinkedHashSet 有序的Set集合 底层实现LinkedHashMap
add() 添加元素
remove() 删除元素
clear() 清空集合
isEmpty() 判断集合长度是否为0
iterator() 获取迭代器
contains(Object o) 判断是否包含某个元素
size() 获取元素个数
常用方法和遍历方式 与HashSet相同
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 15:20
*
* LinkedHashSet 有序的Set集合 底层实现LinkedHashMap
*
* add() 添加元素
* remove() 删除元素
* clear() 清空集合
* isEmpty() 判断集合长度是否为0
* iterator() 获取迭代器
* contains(Object o) 判断是否包含某个元素
* size() 获取元素个数
*
* 常用方法和遍历方式 与HashSet相同
*/
public class TestLinkedHashSet {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("abc2");
set.add("abc1");
set.add("abc3");
set.add("abc4");
set.add("abc5");
set.add("abc6");
for (String s : set) {
System.out.println("s = " + s);
}
System.out.println("---------------------------------------------");
HashSet<String> set1 = new HashSet<>();
set1.add("abc2rerew");
set1.add("abc1revcs");
set1.add("abc3vcvc");
set1.add("abc4;lkljgfwd");
set1.add("abc5dsadsaa");
set1.add("abc6ewqewqe");
for (String s : set1) {
System.out.println("s = " + s);
}
}
}
3.3 TreeSet
TreeSet 有序的set集合 顺序为根据元素比较的顺序
常用方法和遍历方式与HashSet相同
import javax.print.DocFlavor;
import java.util.TreeSet;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 15:25
* TreeSet 有序的set集合 顺序为根据元素比较的顺序
* 常用方法和遍历方式与HashSet相同
*/
public class TestTreeSet {
public static void main(String[] args) {
TreeSet<Integer> set1 = new TreeSet<>();
set1.add(100);
set1.add(20);
set1.add(85);
set1.add(95);
set1.add(77);
set1.add(10);
for (Integer integer : set1) {
System.out.println("integer = " + integer);
}
TreeSet<Student> set2 = new TreeSet<>();
Student stu1 = new Student("赵四1", 32);
Student stu2 = new Student("赵四1", 25);
Student stu3 = new Student("赵四1", 33);
Student stu4 = new Student("赵四1", 63);
set2.add(stu1);
set2.add(stu2);
set2.add(stu3);
set2.add(stu4);
for (Student student : set2) {
System.out.println("student = " + student);
}
}
}
在TreeSet中添加自定义类型的对象 此类必须实现Comprable接口 或者 单独编写类实现Comparator接口
/**
* @author WHD
* @description TODO
* @date 2024/1/20 15:30
*/
public class Person {
String name;
int 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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 15:30
*/
public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
public static void main(String[] args) {
TreeSet<Person> set = new TreeSet<>(new PersonComparator());
set.add(new Person("赵四1", 33));
set.add(new Person("赵四2", 31));
set.add(new Person("赵四3", 35));
set.add(new Person("赵四4", 37));
set.add(new Person("赵四5", 36));
for (Person person : set) {
System.out.println("person = " + person);
}
}
}
4.Map接口
4.1 HashMap
- 常用方法
HashMap常用方法、
size() 获取元素个数
values() 获取集合中所有的值
replace(K key, V value) 替换元素
remove(Object key) 根据键删除元素
put(K key, V value) 添加元素
keySet() 获取所有的键
isEmpty() 判断集合元素个数是否为0
get(Object key) 根据键获取值
entrySet() 获取所有的元素
containsValue(Object value) 判断是否包含某个值
containsKey(Object key) 判断是否包含某个键
clear() 清空集合
import java.util.HashMap;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 15:53
* HashMap常用方法、
* size() 获取元素个数
* values() 获取集合中所有的值
* replace(K key, V value) 替换元素
* remove(Object key) 根据键删除元素
* put(K key, V value) 添加元素
* keySet() 获取所有的键
* isEmpty() 判断集合元素个数是否为0
* get(Object key) 根据键获取值
* entrySet() 获取所有的元素
* containsValue(Object value) 判断是否包含某个值
* containsKey(Object key) 判断是否包含某个键
* clear() 清空集合
*/
public class TestHashMapMethod {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put("CN", "中国");
map.put("RU","俄罗斯");
map.put("KR","小棒棒");
map.put("JP","小日本");
map.put("US","美国");
System.out.println(map.put("CN", "中华人民共和国"));
System.out.println(map.size());
System.out.println("删除键为'JP'的元素:" + map.remove("JP"));
System.out.println("修改键为'KR'的元素:" + map.replace("KR", "小西巴"));
System.out.println("键为CN的值:" + map.get("CN"));
System.out.println("键为RU的值:" + map.get("RU"));
System.out.println("键为US的值:" + map.get("US"));
System.out.println("键为KR的值:" + map.get("KR"));
System.out.println("是否包含为'CN'的键:" +map.containsKey("CN"));
System.out.println("是否包含为'小日本'的值:" +map.containsValue("小日本"));
map.clear();
System.out.println("判断集合长度是否为0:" + map.isEmpty());
}
}
- 遍历方式
HashMap 6种遍历方式
1.获取所有的键 根据键还可以获取值 keySet()
2.获取所有的值 values()
3.获取所有键值对的组合 entrySet()
4.获取所有的键 再根据键获取迭代器 keySet().iterator()
5.获取所有的值的迭代器形式 values().iterator()
6.获取所有键值对的组合的迭代器 entrySet().iterator()
import java.util.*;
/**
* @author WHD
* @description TODO
* @date 2024/1/19 16:03
* HashMap 6种遍历方式
* 1.获取所有的键 根据键还可以获取值 keySet()
* 2.获取所有的值 values()
* 3.获取所有键值对的组合 entrySet()
* 4.获取所有的键 再根据键获取迭代器 keySet().iterator()
* 5.获取所有的值的迭代器形式 values().iterator()
* 6.获取所有键值对的组合的迭代器 entrySet().iterator()
*/
public class TestHashMapForeach {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put("CN", "中国");
map.put("RU","俄罗斯");
map.put("KR","小棒棒");
map.put("JP","小日本");
map.put("US","美国");
// 1.获取所有的键 根据键还可以获取值 keySet()
Set<String> strings = map.keySet();
for (String key : strings) {
System.out.println(key + "====" + map.get(key));
}
System.out.println("----------------------------------------------------");
// 2.获取所有的值 values()
Collection<String> values = map.values();
for (String value : values) {
System.out.println("value = " + value);
}
System.out.println("----------------------------------------------------");
// 3.获取所有键值对的组合 entrySet()
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry);
System.out.println(entry.getKey() + "=" + entry.getValue());
}
System.out.println("----------------------------------------------------");
// 4.获取所有的键 再根据键获取迭代器 keySet().iterator()
Iterator<String> iterator = map.keySet().iterator();
while(iterator.hasNext()){
String key = iterator.next();
System.out.println(key + map.get(key));
}
System.out.println("----------------------------------------------------");
// 5.获取所有的值的迭代器形式 values().iterator()
Iterator<String> iterator1 = map.values().iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}
System.out.println("----------------------------------------------------");
// 6.获取所有键值对的组合的迭代器 entrySet().iterator()
Iterator<Map.Entry<String, String>> iterator2 = map.entrySet().iterator();
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
- 源码和数据结构
HashMap集合特点 : 无序 没有下标 键不能重复 值可以重复 键和值都允许为null 线程不安全
HashMap增删改查效率:比较均衡
回顾之前学习集合的数据结构
ArrayList : 数组 空间连续 查询 修改 快 删除 添加 慢
LinkedList : 双向链表 空间不连续 删除 添加 快 查询 修改 慢*
HashMap数据结构
JDK1.7 数组 + 单向链表
JDK1.8 数组 + 单向链表 + 红黑树
HashMap中的每个元素属于Node对象 Node属于HashMap的内部类 实现了Map.Entry接口
每个Node对象包含四个部分
key值
value值
根据key计算出来的hash值
下一个元素的引用 / 地址
HashMap添加元素过程
当我们调用无参构造方法,将初始化负载因子,和一个指向为null的数组
当我们调用put方法添加元素,将数组的长度初始化为16(懒加载思想)
先根据key计算出来的hash值对数组长度-1进行与运算,得到的数组决定了当前元素应该存放在哪里
如果当前位置没有元素,则直接存放
如果当前位置有元素
先比较是否为同一个key 使用equals和hashCode方法比较
如果是同一个key 则直接覆盖
如果不是同一个key 则再判断向下延伸为链表还是树节点
分别以不同的方式添加
负载因子:0.75F 表示数组的使用率达到75% 数组长度会扩容2倍
树化:当链表的元素个数大于8 并且数组的长度大于等于64 则将单向链表转换为红黑树 因为每次数组扩容以后
集合中的每个元素 都会重新与新的数组长度-1再次进行与运算,所以每次扩容以后,链表中的元素都有50%的概率移动到新的位置,再加上数组使用到75%就扩容这两个因素,所以单向链表转换为红黑树的概率的非常低的,低于千万分之一这个是通过泊松分布统计出来。总结:虽然JDK1.8引入了红黑树结构,但是数组+单向链表依然是常态.
取消树化:当链表的长度小于等于6 则红黑树会再次转换位单向链表
4.2 Hashtable
HashMap和Hashtable的区别?
HashMap允许键和值为null Hashtable不允许键和值为null
HashMap线程不安全 Hashtable线程安全
HashMap初始化(第一次添加元素)数组长度为16 Hashtable初始化数组长度为11
HashMap扩容为2倍 Hashtable扩容为2倍 + 1
两者方法,以及遍历方式都一样
import java.util.Hashtable;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 11:05
* HashMap和Hashtable的区别?
* HashMap允许键和值为null Hashtable不允许键和值为null
* HashMap线程不安全 Hashtable线程安全
* HashMap初始化(第一次添加元素)数组长度为16 Hashtable初始化数组长度为11
* HashMap扩容为2倍 Hashtable扩容为2倍 + 1
*
*
* 两者方法,以及遍历方式都一样
*/
public class TestHashtable {
public static void main(String[] args) {
Hashtable hashtable = new Hashtable();
hashtable.put("a", 111);
hashtable.put("b", 111);
hashtable.put("d", 111);
hashtable.put("c", 111);
hashtable.put("e", 111);
System.out.println(hashtable.containsKey("a"));
System.out.println(hashtable.containsValue(111));
System.out.println(hashtable.get("a"));
System.out.println(hashtable.remove("a"));
System.out.println(hashtable.replace("b", "hello world"));
System.out.println(hashtable.size());
hashtable.clear();
System.out.println(hashtable.isEmpty());
// 遍历方式6种 和HashMap完全相同
}
}
4.3 LinkedHashMap
LinkedHashMap 有序的Map集合 顺序为插入顺序
常用方法和遍历方式 与HashMap完全相同
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 14:00
* LinkedHashMap 有序的Map集合 顺序为插入顺序
* 常用方法和遍历方式 与HashMap完全相同
*/
public class TestLinkedHashMap {
public static void main(String[] args) {
LinkedHashMap<Integer,String> map = new LinkedHashMap<>();
map.put(2, "b");
map.put(3, "c");
map.put(4, "d");
map.put(1, "a");
map.put(5, "e");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println("entry = " + entry);
}
}
}
4.4 TreeMap
TreeMap 一个基于红黑树实现的有序的Map 顺序为根据键比较的顺序
常用方法与遍历方式与HashMap相同
Comparable接口和Comparator接口的区别? Comparable接口属于自然排序 (书写在本来中的排序规则 我们称之为自然排序)
Comparator接口属于非自然排序(单独书写一个类用来指定排序规则 称之为非自然排序)
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 14:05
* TreeMap 一个基于红黑树实现的有序的Map 顺序为根据键比较的顺序
*/
public class TestTreeMap {
public static void main(String[] args) {
TreeMap<Integer,String> map1 = new TreeMap<>();
map1.put(11, "aa");
map1.put(20, "aa");
map1.put(1, "aa");
map1.put(10, "aa");
map1.put(13, "aa");
map1.put(9, "aa");
Set<Map.Entry<Integer, String>> entries = map1.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println( entry);
}
}
}
在实际开发中,如果我们需要将自定义的类对象进行排序 添加到TreeMap集合中
有两种方式
1.在此类上添加实现Comparable接口 重写compareTo方法
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 14:17
*/
public class Student implements Comparable<Student>{
private String name;
private int 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
Student stu1 = new Student("赵四1", 29);
Student stu2 = new Student("赵四2", 26);
Student stu3 = new Student("赵四3", 22);
Student stu4 = new Student("赵四4", 23);
TreeMap<Student,String> map = new TreeMap<>();
map.put(stu1, "a1");
map.put(stu2, "a2");
map.put(stu3, "a3");
map.put(stu4, "a4");
Set<Map.Entry<Student, String>> entries = map.entrySet();
for (Map.Entry<Student, String> entry : entries) {
System.out.println( entry);
}
}
@Override
public int compareTo(Student stu) {
// if(this.getAge() == stu.getAge()){
// return 0;
// }else if(this.getAge() > stu.getAge()){
// return -1;
// }
// return 1;
// return this.getAge() == stu.getAge() ? 0 : (this.getAge() > stu.getAge() ? 1 : -1);
return stu.getAge() - this.getAge() ;
}
}
2.单独编写一个类 实现Comparator接口 重写 compare方法
/**
* @author WHD
* @description TODO
* @date 2024/1/20 14:30
* 在实际开发中,如果我们需要将某个类的对象进行排序 添加到TreeMap集合中
* 有两种方式
* 1.在此类上添加实现Comparable接口 重写compareTo方法
* 2.单独编写一个类 实现Comparator接口 重写 compare方法
*/
public class Person {
private String name;
private int 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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
import java.util.Comparator;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 14:32
*/
public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
}
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 14:33
*
* Comparable接口和Comparator接口的区别?
* Comparable接口属于自然排序 (书写在本来中的排序规则 我们称之为自然排序)
* Comparator接口属于非自然排序(单独书写一个类用来指定排序规则 称之为非自然排序)
*
*/
public class Test {
public static void main(String[] args) {
TreeMap<Person,String> map = new TreeMap<>(new PersonComparator());
Person p1 = new Person("赵四1", 33);
Person p2 = new Person("赵四2", 13);
Person p3 = new Person("赵四3", 23);
Person p4 = new Person("赵四4", 43);
map.put(p1,"aa");
map.put(p2,"aa");
map.put(p3,"aa");
map.put(p4,"aa");
Set<Map.Entry<Person, String>> entries = map.entrySet();
for (Map.Entry<Person, String> entry : entries) {
System.out.println(entry);
}
}
}
4.5 Properties
Properties 继承自Hashtable 此类旨在让用户保存键和值都是字符串的数据
所以我们不能直接调用父类的put或者putAll方法 因为这两个方法允许添加非字符串数据
应该使用setProperty()方法 此方法只能添加键和值都是字符串的数据
import java.util.Properties;
/**
* @author WHD
* @description TODO
* @date 2024/1/20 11:16
*
* Properties 继承自Hashtable 此类旨在让用户保存键和值都是字符串的数据
* 所以我们不能直接调用父类的put或者putAll方法 因为这两个方法允许添加非字符串数据
* 应该使用setProperty()方法 此方法只能添加键和值都是字符串的数据
*/
public class TestProperties {
public static void main(String[] args) {
Properties properties = System.getProperties();
// 调用list方法 将当前Properties对象中的数据全部保存在流中
// System.out就是流对象 此流的作用就是在控制台打印
properties.list(System.out);
Properties properties1 = new Properties();
properties1.setProperty("a1", "abc1");
properties1.setProperty("a2", "abc2");
properties1.setProperty("a3", "abc3");
properties1.setProperty("a4", "abc4");
properties1.setProperty("a5", "abc5");
properties1.list(System.out);
System.out.println(properties1.getProperty("a1"));
System.out.println(properties1.getProperty("a2"));
System.out.println(properties1.getProperty("a3"));
System.out.println(properties1.getProperty("a4"));
System.out.println(properties1.getProperty("a5"));
}
}
5.Collections工具类
Collections是集合工具类 此类中的方法全部为静态方法
binarySearch(List<? extends Comparable<? super T>> list, T key)
使用二分查找 找到指定元素所在的位置 如果没有找到 则返回负数
copy(List<? super T> dest, List<? extends T> src) 将所有元素从一个列表复制到另一个列表中。
fill(List<? super T> list, T obj) 指定元素填充集合
max(Collection<? extends T> coll) 获取集合中的最大元素
min(Collection<? extends T> coll) 获取集合中的最小元素
replaceAll(List list, T oldVal, T newVal) 替换集合中的元素
reverse(List<?> list) 翻转集合
sort(List list) 集合排序
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;
/**
* @author WHD
* @description TODO
* @date 2024/1/22 9:23
* Collections是集合工具类 此类中的方法全部为静态方法
*
* binarySearch(List<? extends Comparable<? super T>> list, T key)
* 使用二分查找 找到指定元素所在的位置 如果没有找到 则返回负数
*
* copy(List<? super T> dest, List<? extends T> src) 将所有元素从一个列表复制到另一个列表中。
*
* fill(List<? super T> list, T obj) 指定元素填充集合
*
* max(Collection<? extends T> coll) 获取集合中的最大元素
*
* min(Collection<? extends T> coll) 获取集合中的最小元素
*
* replaceAll(List<T> list, T oldVal, T newVal) 替换集合中的元素
*
* reverse(List<?> list) 翻转集合
*
* sort(List<T> list) 集合排序
*/
public class TestCollections {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(895);
list1.add(451);
list1.add(889);
list1.add(30);
list1.add(45);
list1.add(450);
Collections.sort(list1);
for (Integer integer : list1) {
System.out.println("integer = " + integer);
}
System.out.println("元素45所在的下标为:" + Collections.binarySearch(list1, 45));
System.out.println("元素666所在的下标为:" + Collections.binarySearch(list1, 666));
List<Integer> list2 = new ArrayList<>();
list2.add(666);
list2.add(666);
list2.add(666);
list2.add(666);
list2.add(666);
list2.add(666);
// 第一个参数为目标数组 第二个参数为原数组
Collections.copy(list2, list1);
for (Integer integer : list2) {
System.out.println("integer = " + integer);
}
System.out.println("----------------------------------------------");
// 将list2集合使用999进行填充
Collections.fill(list2, 999);
for (Integer integer : list2) {
System.out.println("integer = " + integer);
}
System.out.println("----------------------------------------------");
List<Integer> list3 = new ArrayList<>();
list3.add(895);
list3.add(451);
list3.add(889);
list3.add(30);
list3.add(45);
list3.add(450);
System.out.println("list3集合最大元素为:" + Collections.max(list3));
System.out.println("list3集合最小元素为:" + Collections.min(list3));
System.out.println("----------------------------------------------");
Collections.replaceAll(list3, 30, 789);
for (Integer integer : list3) {
System.out.println("integer = " + integer);
}
System.out.println("----------------------------------------------");
Collections.reverse(list3);
for (Integer integer : list3) {
System.out.println("integer = " + integer);
}
System.out.println("----------------------------------------------");
List<Student> stuList = new ArrayList<>();
Student stu1 = new Student("赵四1", 55);
Student stu2 = new Student("赵四2", 35);
Student stu3 = new Student("赵四3", 45);
Student stu4 = new Student("赵四4", 15);
Student stu5 = new Student("赵四5", 25);
stuList.add(stu1);
stuList.add(stu2);
stuList.add(stu3);
stuList.add(stu4);
stuList.add(stu5);
Collections.sort(stuList,new StudentComparator());
for (Student student : stuList) {
System.out.println("student = " + student);
}
}
}
6.泛型
泛型:是JDK1.5引入的新特性 用于统一数据类型 泛型相当于一个占位符 具体类型是暂时未知的 等待恰当的时机来具体化类型
泛型的好处:提高程序的灵活性
泛型的定义/使用位置:类、接口、方法(形参、返回值)
泛型的书写:实际开发中,我们会使用一些带有具体含义的大写字母来定义泛型
T Type 类型
E Element 元素
K Key 键
V Value 值
R Return 返回值
P Parameter 参数
import java.sql.Array;
import java.util.*;
/**
* @author WHD
* @description TODO
* @date 2024/1/22 10:09
* 泛型:是JDK1.5引入的新特性 用于统一数据类型 泛型相当于一个占位符 具体类型是暂时未知的 等待恰当的时机来具体化类型
*
* 泛型的好处:提高程序的灵活性
*
* 泛型的定义/使用位置:类、接口、方法(形参、返回值)
*
* 泛型的书写:实际开发中,我们会使用一些带有具体含义的大写字母来定义泛型
* T Type 类型
* E Element 元素
* K Key 键
* V Value 值
* R Return 返回值
* P Parameter 参数
*
*/
public class TestGenericType {
public static void main(String[] args) {
A a1 = new A();
a1.m1("abc");
System.out.println(a1.m2());
A<String> a2 = new A<>();
a2.m1("112");
System.out.println(a2.m2());
A<Boolean> a3 = new A<>();
a3.m1(true);
System.out.println(a3.m2());
}
}
class A<T>{
public void m1(T t){
System.out.println("t = " + t);
}
public T m2(){
return null;
}
public static <T> T m3(List<T> list){
return list.get(0);
}
public static <T> void m4(T t){}
}
interface B<P,R>{
R m1(P p);
}
class B1 implements B<String,Integer>{
@Override
public Integer m1(String s) {
return Integer.parseInt(s);
}
}
class B2 implements B<Boolean,String>{
@Override
public String m1(Boolean aBoolean) {
return aBoolean.toString();
}
}
class TestB{
public static void main(String[] args) {
B1 b1 = new B1();
System.out.println(b1.m1("123"));
B2 b = new B2();
System.out.println(b.m1(false));
}
}
class Animal{}
class Pet extends Animal{}
class Dog extends Pet{}
class Penguin extends Pet{}
class TwoH extends Dog{}
class TestAnimal{
public static void m1(List<Animal> list){}
public static void m2(Set<Dog> set){}
/**
* ? extends Pet 属于设定泛型的上限 没有设置下限 表示最顶层只到Pet 底层子类 可以是任何直接子类或者间接子类
* @param set
*/
public static void m3(Set<? extends Pet> set){}
/**
* ? super Dog 属于设定泛型下限 没有设置上限 表示最底层是Dog 父类可以是直接父类或者间接父类
* @param list
*/
public static void m4(List<? super Dog> list){}
public static void main(String[] args) {
List<Animal> list1 = new ArrayList<>();
List<Pet> list2 = new Vector<>();
List<Dog> list3 = new LinkedList<>();
List<Penguin> list4 = new Stack<>();
List<TwoH> list5 = new Stack<>();
m4(list1);
m4(list2);
m4(list3);
m1(list1);
Set<Animal> set1 = new HashSet<>();
Set<Pet> set2 = new LinkedHashSet<>();
Set<Dog> set3 = new TreeSet<>();
Set<Penguin> set4 = new HashSet<>();
Set<TwoH> set5 = new HashSet<>();
m2(set3);
m3(set2);
m3(set3);
m3(set4);
m3(set5);
}
}
7.热插拔思想
热插拔思想、面向接口编程
实际开发中 我们推荐使用接口new实现类的方式创建对象 不推荐子类new子类的方式
import java.sql.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
* @author WHD
* @description TODO
* @date 2024/1/22 10:50
* 热插拔思想、面向接口编程
* 实际开发中 我们推荐使用接口new实现类的方式创建对象 不推荐子类new子类的方式
*/
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
// 通过接口new实现类的方式 可以保证通过父类引用调用的方法 任何子类都可以调用
// 所以 我们推荐使用这种方式创建对象 因为后续我们可以灵活的让当前父类引用指向其他的子类对象
// 以实现不同的效果
List list2 = null;
list2.add("a");
list2.add("b");
list2.remove("a");
list2.set(0, "666");
list2.get(0);
}
}
8.红黑树
1.每个节点只能是红色或者黑色。
2.根节点必须是黑色。
3.红色的节点,它的叶节点只能是黑色。
4.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
由以上四个特性我们可以看出一些红黑树的特点:
从根基节点到最远叶节点的路径(最远路径)肯定不会大于根基节点到最近节点的路径(最短路径)的两倍长。这是因为性质3保证了没有相连的红色节点,性质4保证了从这个节点出发的不管是哪一条路径必须有相同数目的黑色节点,这也保证了有一条路径不能比其他任意一条路径的两倍还要长。