第七章 集合
JavaSE学习笔记-chapter07-集合【完结】
学习资源:动力节点java学习
更新日志
- 2024.6.6更新至13小节(手写单向链表)
- 2024.7.16更新至21小节(Properties)
- 2024.7.18更新至26小结(Collections工具类)
完整笔记
笔记1更新至单向链表
笔记2更新至Properties类
笔记3完结更新至Collections工具类
文章目录
- 第七章 集合
- JavaSE学习笔记-chapter07-集合【完结】
- 更新日志
- 完整笔记
- 1. 集合概述
- 2. 集合的继承结构(Java21)
- 3. Collection接口的通用方法
- 4. Collection的通用遍历方式
- 5. SequencedCollection接口
- 6. 泛型(Java5)
- 7. 迭代时删除元素
- 8. List接口
- 9. ArrayList
- 10. Vector
- 11. 链表存储结构
- 12. LinkedList
- 13. 手写单向链表
- 14. 栈
- 15. 队列
- 16. Set
- 17. Map
- 18. HashMap
- 19. LinkedHashMap
- 20. Hashtable
- 21. Properties
- 22. 二叉树
- 23. TreeMap
- 24. Set接口
- 25. HashSet面试题
- 26. Collections工具类
1. 集合概述
2. 集合的继承结构(Java21)
3. Collection接口的通用方法
package addery.zeus.com.collectiontest;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collection;
/**
* Collection接口的通用方法
*/
public class CollectionMethodTest {
// 向集合中添加元素 获取集合中元素个数
@Test
public void testAddAndSize() {
Collection c = new ArrayList();
c.add(10); // 自动装箱
c.add(3.14); // 自动装箱
c.add(false); // 自动装箱
c.add("Addery");
c.add(new Object());
System.out.println(c.size()); // 5
}
// 将参数集合中所有元素全部加入当前集合
@Test
public void testAddAll() {
Collection c1 = new ArrayList();
c1.add(10);
c1.add(3.14);
c1.add(false);
Collection c2 = new ArrayList();
c2.addAll(c1);
System.out.println(c2.size()); // 3
}
// 判断集合中是否包含对象o
@Test
public void testContains() {
Collection c1 = new ArrayList();
c1.add(10);
c1.add(3.14);
c1.add(false);
c1.add("addery");
System.out.println(c1.contains(10)); // true
System.out.println(c1.contains(100)); // false
String s = new String("addery");
// 重写了equals方法,比较时使用equals
System.out.println(c1.contains(s)); // true
}
// 从集合中删除对象o
@Test
public void testRemove() {
Collection c1 = new ArrayList();
c1.add(10);
c1.add(3.14);
c1.add(false);
c1.add("addery");
System.out.println(c1.size()); // 4
System.out.println(c1.remove(10)); // true
System.out.println(c1.size()); // 3
System.out.println(c1.remove(10)); // false
String s = new String("addery");
// 底层会调用equals方法来完成删除
System.out.println(c1.remove(s)); // true
System.out.println(c1.size()); // 2
}
// 清空集合 判断集合元素个数是否为空
@Test
public void testClear() {
Collection c1 = new ArrayList();
c1.add(10);
c1.add(3.14);
c1.add(false);
c1.add("addery");
System.out.println(c1.size()); // 4
System.out.println(c1.isEmpty()); // false
c1.clear();
System.out.println(c1.size()); // 0
System.out.println(c1.isEmpty()); // true
}
// 将集合转换成一维数组
@Test
public void testToArray() {
Collection c1 = new ArrayList();
c1.add(10);
c1.add(3.14);
c1.add(false);
c1.add(new Object());
Object[] array = c1.toArray();
.
for(Object obj : array) {
System.out.println(obj);
}
}
}
4. Collection的通用遍历方式
package addery.zeus.com.collectiontest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Collection接口通用迭代器
*/
public class CollectionTest01 {
public static void main(String[] args) {
// 创建集合对象
Collection c1 = new ArrayList();
// 向集合中添加元素
c1.add(10);
c1.add(3.14);
c1.add(true);
// 创建迭代器对象
Iterator iterator = c1.iterator();
System.out.println(iterator);
// 判断当前光标处是否有元素
while(iterator.hasNext()) {
// 返回当前元素,并将光标向后移动一格
Object next = iterator.next();
System.out.println(next);
}
}
}
5. SequencedCollection接口
package addery.zeus.com.sequncedcollectiontest;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.SequencedCollection;
/**
* SequencedCollection接口常用方法单元测试
* ArrayList LinkedList Vector LinkedHashSet TreeSet Stack都可以调用这个接口中的方法
*/
public class SequencedCollectionMethodTest {
/**
* 向头部添加元素 向末尾添加
*/
@Test
public void testAddFirstAndAddLast() {
SequencedCollection<String> sc = new ArrayList<>();
sc.add("zhangsan1");
sc.add("zhangsan2");
sc.add("zhangsan3");
sc.add("zhangsan4");
sc.add("zhangsan5");
sc.add("zhangsan6");
sc.addFirst("Zeus");
sc.addLast("Addery");
Iterator<String> iterator = sc.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
/**
* 删除头部 删除末尾
*/
@Test
public void testRemoveFirstAndRemoveLast() {
SequencedCollection<String> sc = new ArrayList<>();
sc.add("zhangsan1");
sc.add("zhangsan2");
sc.add("zhangsan3");
sc.add("zhangsan4");
sc.add("zhangsan5");
sc.add("zhangsan6");
sc.removeFirst();
sc.removeLast();
Iterator<String> it = sc.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
/**
* 获取头部节点 获取尾部节点
*/
@Test
public void testGetFirstAndGetLast() {
SequencedCollection<String> sc = new ArrayList<>();
sc.add("zhangsan1");
sc.add("zhangsan2");
sc.add("zhangsan3");
sc.add("zhangsan4");
sc.add("zhangsan5");
sc.add("zhangsan6");
System.out.println(sc.getFirst()); // zhangsan1
System.out.println(sc.getLast()); // zhangsan6
}
/**
* 翻转集合中的元素
*/
@Test
public void testReversed() {
SequencedCollection<String> sc = new ArrayList<>();
sc.add("zhangsan1");
sc.add("zhangsan2");
sc.add("zhangsan3");
sc.add("zhangsan4");
sc.add("zhangsan5");
sc.add("zhangsan6");
SequencedCollection<String> reversed = sc.reversed();
Iterator<String> iterator = reversed.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
6. 泛型(Java5)
6.1 初识泛型
package addery.zeus.com.genericitytest;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void print() {
System.out.println(this.name + "正在打印");
}
}
package addery.zeus.com.genericitytest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class GenericityTest02 {
public static void main(String[] args) {
// 如果不使用泛型
//Collection collection = new ArrayList();
Collection<User> collection = new ArrayList<User>();
// 钻石表达式 省略后面泛型的类型
//Collection<User> collection = new ArrayList<>();
User user1 = new User("zhangsan");
User user2 = new User("wangwu");
collection.add(user1);
collection.add(user2);
//Iterator iterator = collection.iterator();
Iterator<User> iterator = collection.iterator();
while(iterator.hasNext()) {
// 不使用泛型 只能做向下转型
//Object next = iterator.next();
//User next1 = (User) next;
//next1.print();
User next = iterator.next();
next.print();
}
}
}
package addery.zeus.com.genericitytest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class GenericityTest01 {
public static void main(String[] args) {
// 指定了集合中存储元素的类型
Collection<String> collection = new ArrayList<String>();
// 向集合中添加元素
collection.add("addery");
collection.add("zeus");
// 编译器报错 只能向集合中添加String类型元素
//collection.add(10);
// 遍历集合
Iterator<String> iterator = collection.iterator();
while(iterator.hasNext()) {
String next = iterator.next();
// 使用String类型的方法
System.out.println(next.charAt(1));
}
}
}
6.2 泛型的擦除与补偿
6.3 泛型的定义
6.3.1 在类上定义泛型
package addery.zeus.com.genericitytest;
public class Student <S> {
private S name;
public S getName() {
return name;
}
public void setName(S name) {
this.name = name;
}
public Student() {
}
public Student(S name) {
this.name = name;
}
}
package addery.zeus.com.genericitytest;
public class GenericityTest03 {
public static void main(String[] args) {
Student<String> stringStudent = new Student<String>();
stringStudent.setName("zhangsan");
String name = stringStudent.getName();
// 编译器报错
//stringStudent.setName(10);
}
}
package addery.zeus.com.genericitytest;
public class Animals <N, A>{
private N name;
private A age;
public Animals() {
}
public Animals(N name, A age) {
this.name = name;
this.age = age;
}
public N getName() {
return name;
}
public void setName(N name) {
this.name = name;
}
public A getAge() {
return age;
}
public void setAge(A age) {
this.age = age;
}
}
package addery.zeus.com.genericitytest;
public class GenericityTest04 {
public static void main(String[] args) {
Animals<String, Integer> an = new Animals<>();
an.setAge(10);
Integer age = an.getAge();
an.setName("Zeus");
String name = an.getName();
}
}
6.3.2 在静态方法上定义泛型
在类名上的泛型都实在实例化对象时使用,或者说初始化的,但对于静态方法不涉及对象所以无法使用类名上的泛型
package addery.zeus.com.genericitytest;
public class GenericityTest05{
/*
如果在静态方法中使用泛型,则需要在方法返回值类型前面进行泛型的声明。
public static <E> void eatable(E e) {
}
*/
public static <E> void eatable(E[] es) {
for (E e : es) {
System.out.println(e);
}
}
public static void main(String[] args) {
String[] strs = {"zzy", "addery", "zeus"};
eatable(strs);
}
}
6.3.3 在接口上定义泛型
package addery.zeus.com.genericitytest;
public interface Flyable <T>{
void fly(T t);
}
package addery.zeus.com.genericitytest;
// 知道具体的类型
public class Bird implements Flyable<Bird>{
@Override
public void fly(Bird bird) {
}
}
package addery.zeus.com.genericitytest;
// 不知道具体的类型
public class Plane <A> implements Flyable<A>{
@Override
public void fly(A a) {
}
}
接口上使用泛型的区别
package addery.zeus.com.genericitytest;
public interface MyComparable<C> {
int compareTo(C c);
}
package addery.zeus.com.genericitytest;
/**
* 实现接口不使用泛型,方法里的参数列表类型默认为Object,进行比较的时候需要强制类型转化
*/
public class Goods implements MyComparable{
private int price;
public Goods() {
}
public Goods(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public int compareTo(Object o) {
Goods goo = (Goods) o;
return this.price - goo.price;
}
}
package addery.zeus.com.genericitytest;
/**
* 使用泛型不需要再手动进行强制类型转化
*/
public class Goods1 implements MyComparable<Goods1>{
private int price;
public Goods1() {
}
public Goods1(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public int compareTo(Goods1 goo) {
return this.price - goo.price;
}
}
6.4 泛型通配符
package addery.zeus.com.genericitytest;
import java.math.BigDecimal;
import java.util.ArrayList;
public class GenericityTest06 {
public static void print1(ArrayList<?> arrayList) {
// 任意引用类型
}
public static void print2(ArrayList<? extends Number> arrayList) {
// Number及Number的子类
}
public static void print3(ArrayList<? super B> arrayList) {
// B和B的父类
}
public static void main(String[] args) {
print1(new ArrayList<String>());
print1(new ArrayList<Integer>());
print1(new ArrayList<Object>());
print2(new ArrayList<Number>());
print2(new ArrayList<BigDecimal>());
print2(new ArrayList<Double>());
print2(new ArrayList<Integer>());
// 编译器报错
//print2(new ArrayList<Object>());
print3(new ArrayList<B>());
print3(new ArrayList<A>());
print3(new ArrayList<Object>());
// 编译器报错
//print3(new ArrayList<C>());
}
}
class A {
}
class B extends A {
}
class C extends B {
}
7. 迭代时删除元素
package addery.zeus.com.genericitytest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* 迭代时删除元素
*/
public class GenericityTest07 {
public static void main(String[] args) {
// 创建集合对象
Collection<String> c = new ArrayList<>();
// 向集合中添加元素
c.add("zhangsan");
c.add("wangwu");
c.add("lisi");
c.add("wangmazi");
// 遍历集合
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String name = it.next();
if ("lisi".equals(name)) {
// 使用Collection的remove()方法
// ConcurrentModificationException异常。
//c.remove(name);
// 使用迭代器的remove()方法
it.remove();
}
System.out.println(name);
}
}
}
8. List接口
8.1 常用方法
package addery.zeus.com.listtest;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* List接口常用方法单元测试
*
* void add(int index, E element) 在指定索引处插入元素
* E set(int index, E element); 修改索引处的元素
* E get(int index); 根据索引获取元素(通过这个方法List集合具有自己特殊的遍历方式:根据下标遍历)
* E remove(int index); 删除索引处的元素
* int indexOf(Object o); 获取对象o在当前集合中第一次出现时的索引。
* int lastIndexOf(Object o); 获取对象o在当前集合中最后一次出现时的索引。
* List<E> subList(int fromIndex, int toIndex); 截取子List集合生成一个新集合(对原集合无影响)。[fromIndex, toIndex)
*
* static List<E> of(E... elements); 静态方法,返回包含任意数量元素的不可修改列表。(获取的集合是只读的,不可修改的。)
*/
public class ListMethodTest {
// void add(int index, E element) 在指定索引处插入元素
@Test
public void testAdd() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add(1, "李四");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println(list); // [zhangsan, 李四, lisi, wangwu, zhaoliu]
}
// E set(int index, E element); 修改索引处的元素
@Test
public void testSet() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.set(1, "李四");
System.out.println(list); // [zhangsan, 李四, wangwu, zhaoliu]
}
// E remove(int index); 删除索引处的元素
// E get(int index); 根据索引获取元素(通过这个方法List集合具有自己特殊的遍历方式:根据下标遍历)
@Test
public void testRemoveAndGet() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
for (int i = 0; i < list.size(); i++) {
if ("lisi".equals(list.get(i))) {
list.remove(i);
}
}
System.out.println(list);
}
// int indexOf(Object o); 获取对象o在当前集合中第一次出现时的索引。
// int lastIndexOf(Object o); 获取对象o在当前集合中最后一次出现时的索引。
@Test
public void testIndexOfAndLastIndexOf() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add("zhangsan");
System.out.println(list.indexOf("zhangsan")); // 0
System.out.println(list.lastIndexOf("zhangsan")); // 4
}
// List<E> subList(int fromIndex, int toIndex); 截取子List集合生成一个新集合(对原集合无影响)。
// [fromIndex, toIndex)
@Test
public void testSubList() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
List<String> subList = list.subList(1, 3); // [1, 3)
System.out.println(subList); // [lisi, wangwu]
}
// static List<E> of(E... elements); 静态方法,返回包含任意数量元素的不可修改列表。(获取的集合是只读的,不可修改的。)
@Test
public void testOf() {
List<String> list = List.of("1", "2", "3", "Zues");
System.out.println(list);
// 只能读不能修改
//list.set(1, "Addery"); // UnsupportedOperationException
}
}
8.2 特有迭代器
package addery.zeus.com.listtest;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
// List接口特有迭代器
public class ListTest01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
// 获取List集合特有的迭代器
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("=========");
// 从列表中的指定位置开始,获取List集合特有的迭代器
ListIterator<String> it1 = list.listIterator(1);
while (it1.hasNext()) {
System.out.println(it1.next());
}
}
}
package addery.zeus.com.listtest;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/**
* ListIterator接口常用方法单元测试
*
* boolean hasNext(); 判断光标当前指向的位置是否存在元素。
* E next(); 将当前光标指向的元素返回,然后将光标向下移动一位。
* void remove(); 删除上一次next()方法返回的那个数据(删除的是集合中的)。remove()方法调用的前提是:你先调用next()方法。不然会报错。*
*
* void add(E e); 添加元素(将元素添加到光标指向的位置,然后光标向下移动一位。)
* boolean hasPrevious(); 判断当前光标指向位置的上一个位置是否存在元素。
* E previous(); 获取上一个元素(将光标向上移动一位,然后将光标指向的元素返回)
* int nextIndex(); 获取光标指向的那个位置的下标
* int previousIndex(); 获取光标指向的那个位置的上一个位置的下标
* void set(E e); 修改的是上一次next()方法返回的那个数据(修改的是集合中的)。set()方法调用的前提是:你先调用了next()方法。不然会报错。
*/
public class ListIteratorMethodTest {
// void add(E e); 添加元素(将元素添加到光标指向的位置,然后光标向下移动一位。)
@Test
public void testAdd() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
String next = it.next();
if ("lisi".equals(next)) {
it.add("李四"); // 添加后,光标向下移动一位,指向wangwu
}
System.out.println(next); // zhangsan, lisi, wangwu, zhaoliu 不会输出李四
}
System.out.println(list); // [zhangsan, lisi, 李四, wangwu, zhaoliu]
}
// boolean hasPrevious(); 判断当前光标指向位置的上一个位置是否存在元素。
// E previous(); 获取上一个元素(将光标向上移动一位,然后将光标指向的元素返回)
@Test
public void testHasPreviousAndPrevious() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
ListIterator<String> it = list.listIterator();
System.out.println(it.hasPrevious()); // false
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println(it.hasPrevious()); // true
System.out.println(it.previous()); // zhaoliu
}
// int nextIndex(); 获取光标指向的那个位置的下标
// int previousIndex(); 获取光标指向的那个位置的上一个位置的下标
@Test
public void testNextIndexAndPreviousIndex() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
String next = it.next();
if ("lisi".equals(next)) {
System.out.println(it.nextIndex()); // 2
System.out.println(it.previousIndex()); // 1
}
}
}
// void set(E e); 修改的是上一次next()方法返回的那个数据(修改的是集合中的)。
// set()方法调用的前提是:你先调用了next()方法。不然会报错。
@Test
public void testSet() {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
ListIterator<String> it = list.listIterator();
//it.set("addery"); // IllegalStateException
while (it.hasNext()) {
String next = it.next();
if ("lisi".equals(next)) {
it.set("李四");
}
}
System.out.println(list); // [zhangsan, 李四, wangwu, zhaoliu]
}
}
8.3 使用Comparator排序
- 数组排序(要排序的类实现Comparable接口)
package addery.zeus.com.listtest.arraysort;
public class User implements Comparable<User>{
private String name;
private int age;
public User(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 String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(User o) {
return this.getAge() - o.getAge();
}
}
package addery.zeus.com.listtest.arraysort;
import java.util.Arrays;
public class UserSort {
public static void main(String[] args) {
User user1 = new User("abc", 10);
User user2 = new User("abb", 8);
User user3 = new User("bbc", 16);
User user4 = new User("bcc", 13);
User[] users = {user1, user2, user3, user4};
Arrays.sort(users);
System.out.println(Arrays.toString(users));
}
}
- List集合的排序:Comparator方式
package addery.zeus.com.listtest.listsort;
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(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;
}
}
package addery.zeus.com.listtest.listsort;
import java.util.Comparator;
public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
}
package addery.zeus.com.listtest.listsort;
import java.util.ArrayList;
import java.util.List;
public class PersonSort {
public static void main(String[] args) {
Person p1 = new Person("abc", 10);
Person p2 = new Person("abb", 8);
Person p3 = new Person("bbc", 16);
Person p4 = new Person("bcc", 13);
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
list.sort(new PersonComparator());
System.out.println(list);
}
}
- List集合的排序:Comparator方式 + 匿名内部类
package addery.zeus.com.listtest.listsort;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class PersonSort1 {
public static void main(String[] args) {
Person p1 = new Person("abc", 10);
Person p2 = new Person("abb", 8);
Person p3 = new Person("bbc", 16);
Person p4 = new Person("bcc", 13);
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
list.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(list);
}
}
9. ArrayList
-
默认初始化容量**(0)**
-
第一次调用add()方法,底层扩容为长度为10的数组
-
扩容策略
扩容之后的容量是原来容量的1.5倍
10. Vector
-
调用无参构造器,Vector默认初始化长度为10
-
扩容策略,扩大为原来的2倍
11. 链表存储结构
12. LinkedList
-
调用无参构造器,初始化长度为0
-
调用add(“Zeus”)方法默认插入到链表的尾部
-
调用add()方法插入到指定位置
-
修改元素
检查下标是否越界—>找到待修改的结点—>修改—>返回修改前的结点数据
-
删除元素
上述代码中如果删除的是头结点直接first = next;,不需要将next.prev置空吗?
对于头节点的删除操作,确实没有单独的步骤去将新头节点的
prev
置为null
,因为链表的结构维护逻辑已经隐含地处理了这种情况,确保了链接的正确性。 -
获取元素
13. 手写单向链表
13.1 单链表类
package addery.zeus.com.listtest;
/**
* 单向链表
*/
public class MyLinkedList<E> {
/**
* 链表中结点的个数
*/
private int size;
/**
* 链表的头结点
*/
private Node<E> first;
public MyLinkedList() {
}
/**
* 默认的添加方式
* @param data 待添加的数据
*/
public boolean add(E data) {
addLast(data);
return true;
}
/**
* 添加至指定位置
* @param index 待添加的位置
* @param data 待添加的数据
*/
public void add(int index, E data) {
checkPositionIndex(index);
if (index == size)
addLast(data);
else
addAppoint(data, node(index), index);
}
/**
* 删除指定索引处的结点,并返回原结点的数据
* @param index 待删除的索引
* @return 待删除索引的数据
*/
public E remove(int index) {
checkPositionIndex(index);
Node<E> oldNode = node(index);
E oldNodeData = oldNode.item;
if (index == 0) { // 如果删除的是头结点
first = oldNode.next;
}else { // 非头结点
Node<E> oldNodePrev = node(index - 1);
oldNodePrev.next = oldNode.next;
}
oldNode.next = null;
oldNode.item = null;
size--;
return oldNodeData;
}
/**
* 修改指定索引处结点的数据,并返回原始数据
* @param index 待修改的结点索引
* @param data 新的结点数据
* @return 待修改结点的原始数据
*/
public E set(int index, E data) {
checkPositionIndex(index);
Node<E> oldNode = node(index);
E oldData = oldNode.item;
oldNode.item = data;
return oldData;
}
/**
* 获取指定结点处的元素数据
* @param index 待查找的索引
* @return 待查找索引的数据
*/
public E get(int index) {
checkPositionIndex(index);
return node(index).item;
}
/**
* 默认添加至末尾
*/
public void addLast(E data) {
Node<E> newNode = new Node<>(data, null);
if (first == null)
first = newNode;
else
node(size - 1).next = newNode;
size++;
}
/**
* 添加结点至指定位置
* @param data 待添加的结点数据
* @param in 目标位置的结点对象
* @param index 目标位置索引
*/
void addAppoint(E data, Node<E> in, int index) {
Node<E> newNode = new Node<>(data, in);
if (index == 0) { // 如果在头结点处添加结点
first = newNode;
} else { // 非头结点处
Node<E> inPrevNode = node(index - 1);
inPrevNode.next = newNode;
}
size++;
}
/**
* 查找结点,返回结点对象
* @param index 待查找到结点索引
* @return 目标节点对象
*/
Node<E> node(int index) {
Node<E> x = first;
for(int i = 0; i < index; i++) {
x = x.next;
}
return x;
}
/**
* 检查下标
* @param index 下标
*/
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 下标是否在合理范围内
* @param index 下标
* @return 合理true,不合理false
*/
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
/**
* 构造IndexOutOfBoundsException的提示信息
* @param index 下标
* @return 提示信息:String
*/
private String outOfBoundsMsg(int index) {
return "Index:" + index + ", Size:" + size;
}
/**
* 获取链表中结点的个数
* @return 结点个数
*/
public int getSize() {
return size;
}
/**
* 内部类:单链表的结点
*/
private static class Node<E> {
/**
* 结点数据
*/
E item;
/**
* 下一个结点
*/
Node<E> next;
Node(E item, Node<E> next) {
this.item = item;
this.next = next;
}
}
}
13.2 Junit单元测试
package addery.zeus.com.listtest;
import org.junit.jupiter.api.Test;
/**
* MyLinkedList自定义单链表方法单元测试
*/
public class MyLinkedListMethodTest {
// add()
@Test
public void testAdd() {
MyLinkedList<String> mll = new MyLinkedList<>();
mll.add("Zeus");
mll.add("Stone");
mll.add("Apple");
mll.add("pear");
mll.add("peach");
mll.add(2, "Addery");
//mll.add(10, "Addery"); // java.lang.IndexOutOfBoundsException: Index:10, Size:6
for (int i = 0; i < mll.getSize(); i++) {
System.out.println(mll.get(i));
}
}
// test()
@Test
public void testSet() {
MyLinkedList<String> mll = new MyLinkedList<>();
mll.add("Zeus");
mll.add("Stone");
mll.add("Apple");
mll.add("pear");
mll.add("peach");
String oldData = mll.set(2, "Addery");
System.out.println(oldData); // Apple
for (int i = 0; i < mll.getSize(); i++) {
System.out.println(mll.get(i));
}
}
// remove()
@Test
public void testRemove() {
MyLinkedList<String> mll = new MyLinkedList<>();
mll.add("Zeus");
mll.add("Stone");
mll.add("Apple");
mll.add("pear");
mll.add("peach");
String oldData = mll.remove(1);
System.out.println(oldData);
mll.remove(0);
mll.add("z");
for (int i = 0; i < mll.getSize(); i++) {
System.out.println(mll.get(i));
}
}
// get()
@Test
public void testGet() {
MyLinkedList<String> mll = new MyLinkedList<>();
mll.add("Zeus");
mll.add("Stone");
mll.add("Apple");
mll.add("pear");
mll.add("peach");
System.out.println(mll.get(2));
System.out.println("======");
for (int i = 0; i < mll.getSize(); i++) {
System.out.println(mll.get(i));
}
}
}
14. 栈
package addery.zeus.com.stacktest;
import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.Stack;
public class stackTest01 {
public static void main(String[] args) {
// 创建stack对象
Stack<String> stack0 = new Stack<>();
LinkedList<String> stack1 = new LinkedList<>();
ArrayDeque<String> stack2 = new ArrayDeque<>();
// 栈相关操作测试
// 1. 压栈
stack0.push("s");
stack0.push("z");
stack0.push("y");
System.out.println(stack0.search("z")); // 2
stack1.push("1");
stack1.push("2");
stack1.push("3");
stack2.push("1");
stack2.push("2");
stack2.push("3");
// 2.弹栈,将栈顶元素删除,并返回被删除的引用
System.out.println(stack0.pop()); // y
System.out.println(stack1.pop()); // 3
System.out.println(stack2.pop()); // 3
// 窥视栈顶元素
System.out.println(stack0.peek()); // z
// 查找栈中元素
System.out.println(stack0.search("z")); // 1,以1开始,从栈顶元素往下数
}
}
15. 队列
package addery.zeus.com.queuetest;
import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.Queue;
/**
* Queue接口基于Collection扩展的方法包括:
* boolean offer(E e); 入队。
* E poll(); 出队,如果队列为空,返回null。
* E remove(); 出队,如果队列为空,抛异常。
* E peek(); 查看队头元素,如果为空则返回null。
* E element(); 查看对头元素,如果为空则抛异常。
*/
public class queueTest01 {
public static void main(String[] args) {
// 创建队列对象
Queue<String> queue1 = new LinkedList<>();
Queue<String> queue2 = new ArrayDeque<>();
// 入队
queue1.offer("element1");
queue1.offer("element2");
System.out.println(queue1.offer("element3"));
queue2.offer("element1");
queue2.offer("element2");
System.out.println(queue2.offer("element3"));
// 出队
System.out.println("--------------LinkedList------------");
String poll = queue1.poll();
System.out.println(poll); // element1
System.out.println(queue1.poll()); // element2
System.out.println("--------------ArrayDeque------------");
System.out.println(queue2.poll());
System.out.println(queue2.poll());
System.out.println(queue2.poll());
// System.out.println(queue2.poll()); // null
// System.out.println(queue2.remove()); // NoSuchElementException
// peek()
System.out.println("--------------peek()------------");
System.out.println(queue1.peek());
System.out.println(queue2.peek()); // null
// element()
System.out.println(queue2.element()); // NoSuchElementException
}
}
package addery.zeus.com.queuetest;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
/**
* boolean offerLast(E e); 从队尾入队
* E pollFirst(); 从队头出队
* boolean offerFirst(E e); 从队头入队
* E pollLast(); 从队尾出队
*/
public class dequeTest01 {
public static void main(String[] args) {
// 创建双端队列
Deque<String> deque1 = new LinkedList<>();
Deque<String> deque2 = new ArrayDeque<>();
// 从队尾进,从队头出
System.out.println("------------从队尾进,从队头出--------------");
deque1.offer("z1");
deque1.offer("z2");
deque1.offer("z3");
System.out.println(deque1.poll());
System.out.println(deque1.poll());
System.out.println(deque1.poll());
System.out.println("------------从队尾进,从队头出--------------");
deque1.offerLast("a1");
deque1.offerLast("a2");
deque1.offerLast("a3");
System.out.println(deque1.pollFirst());
System.out.println(deque1.pollFirst());
System.out.println(deque1.pollFirst());
// 从队头进,从队尾出
System.out.println("-------------从队头进,从队尾出-----------------");
deque1.offerFirst("f1");
deque1.offerFirst("f2");
deque1.offerFirst("f3");
System.out.println(deque1.pollLast());
System.out.println(deque1.pollLast());
System.out.println(deque1.pollLast());
// 从队尾进,从队尾出 模拟栈
System.out.println("------------从队尾进,从队尾出--------------");
deque2.offerLast("ff1");
deque2.offerLast("ff2");
deque2.offerLast("ff3");
System.out.println(deque2.pollLast());
System.out.println(deque2.pollLast());
System.out.println(deque2.pollLast());
}
}
16. Set
底层都是用Map实现的
17. Map
17.1 Map继承结构
17.2 Map接口的常用方法
package addery.zeus.com.maptest;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* V put(K key, V value); 添加键值对
* void putAll(Map<? extends K,? extends V> m); 添加多个键值对
* V get(Object key); 通过key获取value
* boolean containsKey(Object key); 是否包含某个key
* boolean containsValue(Object value); 是否包含某个value
* V remove(Object key); 通过key删除key-value
* void clear(); 清空Map
* int size(); 键值对个数
* boolean isEmpty(); 判断是否为空Map
* Collection<V> values(); 获取所有的value
* Set<K> keySet(); 获取所有的key
* Set<Map.Entry<K,V>> entrySet(); 获取所有键值对的Set视图。
* static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3); 静态方法,使用现有的key-value构造Map
*/
public class MapMethodTest {
// V put(K key, V value); 添加键值对
// void putAll(Map<? extends K,? extends V> m); 添加多个键值对
@Test
public void testPutAndPutAll() {
Map<Integer, String> map = new HashMap<>();
System.out.println("-----------put------------");
map.put(1, "zeus");
map.put(2, "zzy");
System.out.println(map);
System.out.println("----------putAll-----------");
Map<Integer, String> map1 = new HashMap<>();
map1.put(3, "test");
map1.putAll(map);
System.out.println(map1);
System.out.println("-----Map<? extends K,? extends V> m-----");
}
// V get(Object key); 通过key获取value
@Test
public void testGet() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "zeus");
map.put(2, "zzy");
System.out.println(map.get(1)); // zeus
}
// * boolean containsKey(Object key); 是否包含某个key
// * boolean containsValue(Object value); 是否包含某个value
@Test
public void testContainsKeyAndContainsValue() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "zeus");
map.put(2, "zzy");
map.put(3, "stone");
System.out.println("------------containsKey-------------");
System.out.println(map.containsKey(1));
System.out.println(map.containsKey(4));
System.out.println("------------containsValue-------------");
System.out.println(map.containsValue("zzy"));
System.out.println(map.containsValue("aiohfo"));
}
// * V remove(Object key); 通过key删除key-value
// * void clear(); 清空Map
// boolean isEmpty(); 判断是否为空Map
@Test
public void testRemoveAndClearAndIsEmpty() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "zeus");
map.put(2, "zzy");
map.put(3, "stone");
System.out.println(map.remove(1)); // 返回值为Value:zeus
System.out.println(map);
System.out.println(map.isEmpty()); // false
map.clear(); // {2=zzy, 3=stone}
System.out.println(map); // {}
System.out.println(map.isEmpty()); // true
}
// int size(); 键值对个数
@Test
public void testSize() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "zeus");
map.put(2, "zzy");
System.out.println(map.size()); // 2
map.put(3, "stone");
System.out.println(map.size()); // 3
}
// * Collection<V> values(); 获取所有的value
// * Set<K> keySet(); 获取所有的key
@Test
public void testValuesAndKeySet() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "zeus");
map.put(2, "zzy");
map.put(3, "stone");
System.out.println("values:" + map.values());
System.out.println("key:" + map.keySet());
}
// * static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3); 静态方法,使用现有的key-value构造Map
@Test
public void testMap() {
Map<Integer, String> map = Map.of(1, "z", 2, "z", 3, "y");
System.out.println(map);
}
// * Set<Map.Entry<K,V>> entrySet(); 获取所有键值对的Set视图。
@Test
public void testEntrySet() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "zeus");
map.put(2, "zzy");
map.put(3, "stone");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
System.out.println(entries);
}
}
17.3 Map集合的遍历方法
package addery.zeus.com.maptest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 遍历map集合的方法
*/
public class MapTest01 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "z");
map.put(2, "y");
map.put(4, "d");
map.put(3, "g");
// 第一种方式,拿到map集合所有的key,通过key去找value
System.out.println("----第一种方式----");
Set<Integer> keys = map.keySet();
Iterator<Integer> it = keys.iterator();
while (it.hasNext()) {
Integer key = it.next();
System.out.println(map.get(key));
}
System.out.println("----foreach----");
for (Integer key : keys) {
System.out.println(map.get(key));
}
// 第二种方式通过entrySet()方法
System.out.println("----第二种方法entrySet---");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> next = iterator.next();
Integer key = next.getKey();
String value = next.getValue();
System.out.println(key + "=" + value);
}
System.out.println("----foreach----");
for (Map.Entry<Integer, String> en : entries) {
System.out.println(en.getKey() + "=" + en.getValue());
}
}
}
18. HashMap
18.1 概述
18.2 存储原理
18.3 手写put()和get()方法
package addery.zeus.com.hashmap;
/**
* 手写HashMap类的put()和get()方法
*/
public class MyHashMap<K, V> {
/**
* 哈希表
*/
private Node<K, V>[] table;
/**
* 哈希表的长度
*/
private int size;
@SuppressWarnings("unchecked")
public MyHashMap() {
// 在new数据的时候不能使用泛型,new Node<V, K>[16]
this.table = new Node[16];
}
public int getSize() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < table.length; i++) {
Node<K, V> node = table[i];
// 如果table[i]不为空,遍历整个单链表
while (null != node) {
sb.append(node);
sb.append(", ");
node = node.next;
}
}
return sb.toString();
}
static class Node<K, V> {
/**
* 值
*/
private V value;
/**
* 键
*/
private K key;
/**
* 哈希值
*/
private int hash;
/**
* 下一个结点
*/
private Node<K, V> next;
public Node() {
}
public Node(V value, K key, int hash, Node<K, V> next) {
this.value = value;
this.key = key;
this.hash = hash;
this.next = next;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public int getHash() {
return hash;
}
public void setHash(int hash) {
this.hash = hash;
}
public Node<K, V> getNext() {
return next;
}
public void setNext(Node<K, V> next) {
this.next = next;
}
@Override
public String toString() {
return "[" + "value=" + value + ", key=" + key + "]";
}
}
/**
* 【第一步】:处理key为null的情况
* 如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置。
* 【第二步】:获得key对象的哈希值
* 如果添加键值对的key不是null,则就调用key的hashcode()方法,获得key的哈希值。
* 【第三步】:获得键值对的存储位置
* 因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
* 那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
* 【第四步】:将键值对添加到table数组中
* 当table[i]返回结果为null时,则键键值对封装为Node对象并存入到table[i]的位置。
* 当table[i]返回结果不为null时,则意味着table[i]存储的是单链表。我们首先遍历单链表,如果遍历出来节点的
* key和添加键值对的key相同,那么就执行覆盖操作;如果遍历出来节点的key和添加键值对的key都不同,则就将键键
* 值对封装为Node对象并插入到单链表末尾。
*
* @param value 值
* @param key 键
*/
public V put(K key, V value) {
// 如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置。
if (null == key) {
return addForNullKey(value);
}
// 如果key不是null
int code = key.hashCode();
int tableIndex = Math.abs(code % table.length);
// 如果table[tableIndex]还没有数据,直接添加
Node<K, V> node = table[tableIndex];
if (null == node) {
Node<K, V> newNode = new Node<>(value, key, code, null);
table[tableIndex] = newNode;
size++;
return value;
}
// 如果程序执行到这里,说明table[tableIndex]存在数据,则需要遍历链表
Node<K, V> preNode = null;
while (null != node) {
if (key.equals(node.getKey())) {
V oldValue = node.getValue();
node.setValue(value);
return oldValue;
}
preNode = node;
node = node.next;
}
Node<K, V> newNode = new Node<>(value, key, code, null);
preNode = newNode;
size++;
return value;
}
/**
* key为null的情况
*
* @param value 值
* @return oldValue或者value
*/
private V addForNullKey(V value) {
// 如果table[0]还没有数据,直接添加
Node<K, V> node = table[0];
if (null == node) {
Node<K, V> newNode = new Node<>(value, null, 0, null);
table[0] = newNode;
size++;
return value;
}
// 如果程序执行到这里,说明table有单链表,需要遍历单链表,寻找key为null的结点
Node<K, V> preNode = null;
while (null != node) {
if (null == node.getKey()) {
V oldValue = node.getValue();
node.setValue(value);
return oldValue;
}
preNode = node;
node = node.next;
}
// 如果程序执行到这里,说明table[0]存在单链表,但是链表中不存在key为null的结点
Node<K, V> newNode = new Node<>(value, null, 0, null);
preNode.next = newNode;
size++;
return value;
}
/**
* 【第一步】:处理key为null的情况
* 如果查询的key就是null,则就在table数组索引为0的位置去查询。
* 【第二步】:获得key对象的哈希值
* 如果查询的key不是null,则就调用key的hashcode()方法,获得key的哈希值。
* 【第三步】:获得键值对的存储位置
* 因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
* 那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
* 【第四步】:遍历单链表,根据key获得value值
* 如果table[i]返回的结果为null,则证明单链表不存在,那么返回null即可
* 如果table[i]返回的结果不为null时,则证明单链表存在,那么就遍历整个单链表。如果遍历出来节点的key和查询
* 的key相同,那么就返回遍历出来节点的value值;如果整个单链表遍历完毕,则遍历出来节点的key和查询的key都不
* 相等,那么就证明查询key在链表中不存在,则直接返回null即可。
* @param key 键
* @return 值
*/
public V get(K key) {
// 如果查询的key就是null,则就在table数组索引为0的位置去查询。
if (null == key) {
Node<K, V> node = table[0];
if (null == node) {
return null;
}
// 有单链表,遍历
while(node != null) {
if (null == node.getKey()) {
return node.getValue();
}
node = node.next;
}
return null;
}
// 如果key不是null
int code = key.hashCode();
int tableIndex = Math.abs(code % table.length);
Node<K, V> node = table[tableIndex];
// table[tableIndex]处不存在数据
if (null == node) {
return null;
}
// 如果程序运行到此处,说明table[tableIndex]处存在单链表
while (null != node) {
if (key.equals(node.getKey())) {
return node.getValue();
}
node = node.next;
}
return null;
}
}
package addery.zeus.com.hashmap;
import java.util.Objects;
public class User {
private String name;
private int age;
public User() {
}
public User(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 String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
package addery.zeus.com.hashmap;
/**
* 手写HashMap的get和put方法测试程序
*/
public class HashMapTest01 {
public static void main(String[] args) {
System.out.println("------------put()------------");
MyHashMap<String, String> map = new MyHashMap<>();
map.put("张三", "111");
map.put("李四", "222");
map.put("王五", "333");
map.put("赵六", "444");
map.put("赵六", "555");
map.put(null, "666");
System.out.println(map);
System.out.println("------------get()------------");
System.out.println(map.get("张三"));
System.out.println(map.get("赵六"));
System.out.println(map.get(null));
System.out.println(map.get("abc"));
System.out.println("------------自定义数据类型------------");
MyHashMap<User, Integer> userMap = new MyHashMap<>();
User user1 = new User("张三", 18);
User user2 = new User("李四", 18);
User user3 = new User("王五", 18);
User user4 = new User("赵六", 18);
User user5 = new User("赵六", 19);
User user6 = new User("赵六", 19);
userMap.put(user1, 1);
userMap.put(user2, 2);
userMap.put(user3, 3);
userMap.put(user4, 4);
userMap.put(user5, 4);
userMap.put(user6, 5);
System.out.println(userMap);
}
}
18.4 HashMap在Java8后的改进(包括Java8)
18.5 HashMap初始化容量
19. LinkedHashMap
package addery.zeus.com.linkedhashmaptest;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* 哈希表 + 双向链表
*/
public class LinkedHashMapTest01 {
public static void main(String[] args) {
LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put(1, "a");
linkedHashMap.put(4543, "b");
linkedHashMap.put(222, "c");
linkedHashMap.put(657, "d");
linkedHashMap.put(888, "e");
linkedHashMap.put(888, "eeee");
Set<Map.Entry<Integer, String>> entries = linkedHashMap.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
}
20. Hashtable
package addery.zeus.com.hastabletest;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
/**
* Hashtable是线程安全的
* Hashtable初始化容量为11
* Hashtable扩容也是2倍
* Hashtable的key和value都不能为null,否则会空指针异常
* Hashtable特有的方法
* Enumeration keys = hashtable.keys()
* Enumeration elements = hashtable.elements()
* Enumeration的方法
* boolean has = enumeration.hasMoreElements()
* K/V e = enumeration.nextElement()
*/
public class HashtableTest01 {
public static void main(String[] args) {
Hashtable<Integer, String> map = new Hashtable<>();
// NullPointerException
// map.put(null, "zzz");
// map.put(1, null);
// map.put(null, null);
map.put(1, "z");
map.put(2, "z");
map.put(3, "y");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
Enumeration<Integer> keys = map.keys();
while (keys.hasMoreElements()) {
Integer i = keys.nextElement();
System.out.println(i);
}
Enumeration<String> elements = map.elements();
while (elements.hasMoreElements()) {
String s = elements.nextElement();
System.out.println(s);
}
}
}
21. Properties
package addery.zeus.com.propertiestest;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Properties不支持泛型,K和V只能是String类型
* setProperties() (put())
* getProperties() (get())
* Enumeration<?> e = properties.propertyNames() (keys())
*/
public class PropertiesTest01 {
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("zzy", "张志毅");
properties.setProperty("zeus", "宙斯");
System.out.println(properties.getProperty("zzy"));
System.out.println(properties.getProperty("zeus"));
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
Enumeration<Object> keys = properties.keys();
while (keys.hasMoreElements()) {
String s = (String)keys.nextElement();
System.out.println(s);
}
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String s = (String)enumeration.nextElement();
System.out.println(s);
}
}
}
22. 二叉树
22.1. 排序二叉树
22.2. 平衡二叉树
22.3. 红黑二叉树(自平衡的排序二叉树)
23. TreeMap
package addery.zeus.com.treemaptest;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
/**
* TreeMap底层是红黑二叉树
* TreeMap是有序不可重复的,有序是指 可排序的
* TreeMap的k不允许为空
*/
public class TreeMapTest01 {
public static void main(String[] args) {
Map<Integer, String> treeMap = new TreeMap<>();
treeMap.put(4, "4");
treeMap.put(2, "2");
treeMap.put(1, "1");
treeMap.put(6, "6");
treeMap.put(3, "3");
treeMap.put(5, "5");
treeMap.put(5, "55");
System.out.println(treeMap); // {1=1, 2=2, 3=3, 4=4, 5=55, 6=6}
System.out.println("------------自定义数据类型------------");
/**
* java.lang.ClassCastException: class addery.zeus.com.treemaptest.User cannot be cast to class java.lang.Comparable
* 自定义数据类型未实现comparable接口,重写compareTo方法,也没有在构造userTreeMap时传入Comparator对象会报类型转换异常
* 这里可以看一小爱TreeMap的源码,在put元素的时候需要判断有无Comparator对象,如果没有会将put的对象转化为Comparable对象,在这里就会报出ClassCastException
* 比如:如果第一次put会调用addEntryToEmptyMap(key, value)方法,在这个方法中会调用compare()方法,在这个方法中也是上述的判断逻辑
*/
System.out.println("---方法1:使用实现Comparable接口的类型---");
Map<User, String> userTreeMap = new TreeMap<>();
User user1 = new User("张三", 18);
User user2 = new User("李四", 16);
User user3 = new User("王五", 19);
User user4 = new User("赵六", 38);
User user5 = new User("小红", 27);
userTreeMap.put(user1, "1");
userTreeMap.put(user2, "2");
userTreeMap.put(user3, "3");
userTreeMap.put(user4, "4");
userTreeMap.put(user5, "5");
System.out.println(userTreeMap);
System.out.println("---方法2:使用Comparator比较器对象的类型---");
VipComparator vipComparator = new VipComparator();
Map<Vip, String> vipTreeMap = new TreeMap<>(vipComparator);
Vip vip1 = new Vip("张三", 18);
Vip vip2 = new Vip("李四", 16);
Vip vip3 = new Vip("王五", 19);
Vip vip4 = new Vip("赵六", 38);
Vip vip5 = new Vip("小红", 27);
vipTreeMap.put(vip1, "1");
vipTreeMap.put(vip2, "2");
vipTreeMap.put(vip3, "3");
vipTreeMap.put(vip4, "4");
vipTreeMap.put(vip5, "5");
System.out.println(vipTreeMap);
System.out.println("---方法3:使用Comparator比较器对象的类型,匿名内部类方式---");
Map<Vip, String> vipTreeMap2 = new TreeMap<>(new Comparator<Vip>() {
@Override
public int compare(Vip o1, Vip o2) {
return o1.getAge() - o2.getAge();
}
});
Vip vipp1 = new Vip("张三", 18);
Vip vipp2 = new Vip("李四", 16);
Vip vipp3 = new Vip("王五", 19);
Vip vipp4 = new Vip("赵六", 38);
Vip vipp5 = new Vip("小红", 27);
vipTreeMap2.put(vipp1, "1");
vipTreeMap2.put(vipp2, "2");
vipTreeMap2.put(vipp3, "3");
vipTreeMap2.put(vipp4, "4");
vipTreeMap2.put(vipp5, "5");
System.out.println(vipTreeMap2);
// TreeMap的k不允许为空 NullPointerException
// vipTreeMap2.put(null, "5");
vipTreeMap2.put(vipp5, null);
}
}
class Vip { /*---Vip类---*/
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 Vip(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vip vip = (Vip) o;
return age == vip.age && Objects.equals(name, vip.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Vip{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class VipComparator implements Comparator<Vip> { /*---Vip类的比较器---*/
@Override
public int compare(Vip o1, Vip o2) {
return o1.getAge() - o2.getAge();
}
}
package addery.zeus.com.treemaptest;
import java.util.Objects;
/**
* 用户类
*/
public class User implements Comparable<User> {
private String name;
private int age;
public User(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;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(User o) {
return this.age - o.age;
}
}
24. Set接口
HashSet、LinkedHashSet、TreeSet
25. HashSet面试题
package addery.zeus.com.settest;
import java.util.HashSet;
public class HashSetTest02 {
public static void main(String[] args) {
// 创建一个存放Student类型数据的HashSet对象
HashSet<Student> set = new HashSet<>();
// 创建一个学生对象
Student stu = new Student("张三", 18);
// 向set中添加学生对象 张三 18
set.add(stu);
// 向set中添加学生对象 李四 21
set.add(new Student("李四", 21));
// 修改学生 stu的name 张三 ---> 王五
stu.setName("王五");
// 问题1:请问是否删除了HashSet集合中的stu对象呢???
/**
* 分析:HashSet集合中存放的是stu对象的引用,所以修改stu对象的name属性,在集合中也会被修改
* 但是集合中修改的是的stu对象的name属性,并未修改其哈希值,因为哈希值是在该stu插入集合时生
* 成的,而其哈希值的生成是根据name和age属性,所以其哈希值还是根据原始name 张三和age生成的
* 结论:故此,remove(stu)时,会根据传入的Student类型对象的name和age属性计算哈希值,通
* 过哈希值计算其再哈希表的所在位置,然后在链表中查找name为“王五”,age为“18”的Student对
* 象,但由于其哈希值和根据新的name 王五 计算的,不一定为找到原来的位置,所以不会删除该对象
*/
set.remove(stu);
System.out.println(set); // [Student{name='王五', age='18'}, Student{name='李四', age='21'}]
// 问题2:添加以下Student对象是否成功???
/**
* 分析:根据问题1的分析,插入new Student("王五", 18)对象生成的哈希值和stu不同
* 结论:所以会插入成功
*/
set.add(new Student("王五", 18));
System.out.println(set); // [Student{name='王五', age='18'}, Student{name='王五', age='18'}, Student{name='李四', age='21'}]
// 问题3:添加以下Student对象是否成功???
/**
* 分析:基于问题1的分析,插入new Student("张三", 18)对象生成的哈希值和stu相同
* 但是,哈希值相同只能说明在哈希表的相同索引处的链表中,在链表中还需要使用equals方法比较
* 对象是否重复,我们的Student类中重写的equals是通过name和age共同判断的,所以这里equals
* 方法返回False
* 结论:会插入成功,并且在哈希表的相同位置
*/
set.add(new Student("张三", 18));
System.out.println(set); // [Student{name='王五', age='18'}, Student{name='王五', age='18'}, Student{name='张三', age='18'}, Student{name='李四', age='21'}]
}
}
package addery.zeus.com.settest;
import java.util.Objects;
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
26. Collections工具类
Collections工具类提供了很多操作集合的方法,如下图是几个比较常用的。
package addery.zeus.com.collectionstest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Collections工具类方法测试
*/
public class CollectionsTest01 {
public static void main(String[] args) {
System.out.println("---sort()---");
List<Integer> list = Arrays.asList(6, 4, 2, 5, 1, 3);
System.out.println(list); // [6, 4, 2, 5, 1, 3]
Collections.sort(list);
System.out.println(list); // [1, 2, 3, 4, 5, 6]
System.out.println("---shuffle()打乱顺序---");
Collections.shuffle(list);
System.out.println(list); // [4, 3, 6, 1, 2, 5]
Collections.shuffle(list);
System.out.println(list); // [3, 1, 5, 6, 2, 4]
System.out.println("---reverse()反转---");
Collections.sort(list);
System.out.println(list); // [1, 2, 3, 4, 5, 6]
Collections.reverse(list);
System.out.println(list); // [6, 5, 4, 3, 2, 1]
System.out.println("---fill()替换所有元素---");
Collections.fill(list, 666);
System.out.println(list); // [666, 666, 666, 666, 666, 666]
}
}