集合框架的概述
集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
数组的特点:
一旦初始化后,其长度就确定了。
数组一旦定义好,其元素类型也就确定了。
数组存储的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
Java集合可分为Collection 和 Map 两种体系
- Collection接口:单列数据,定义了存取一组对象的方法的集合
- List: 元素有序、可重复的集合 (动态数组) -->实现类:ArrayList、LinkedList、Vector
- Set: 元素无序、不可重复的集合 -->实现类:HashSet、LinkedHashSet、TreeSet - Map 接口: 双列数据,保存具有映射关系"key-value" 的集合 -->实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properies
Collection接口
方法:
import org.junit.Test;
import java.util.*;
import java.util.List;
/**
* @Author: mei_ming
* @DateTime: 2022/6/25 17:45
*/
public class CollectionTest {
@Test
public void test01(){
//1.add() : 添加元素
Collection coll = new ArrayList();
coll.add(13); //自动装箱
coll.add("str");
coll.add(13.5f);
coll.add(new Date());
//2.size() : 获取添加的元素个数
System.out.println(coll.size()); //4
Collection coll2 = new ArrayList();
coll2.add(13);
coll2.add("str");
coll2.add(13.5f);
coll.addAll(coll2);
System.out.println(coll.size()); //7
System.out.println(coll); //[13, str, 13.5, Sat Jun 25 17:52:17 CST 2022, 13, str, 13.5]
//3.clear() : 清空集合元素
coll.clear();
System.out.println(coll); //[]
//4.isEmpty() : 判空
System.out.println(coll.isEmpty()); //true
}
@Test
public void test02(){
Collection coll = new ArrayList();
coll.add(13); //自动装箱
coll.add("str");
coll.add(13.5f);
coll.add(new String("abc"));
coll.add(new Person("Tom",12));
//1. contains() : 判断当前集合中是否包含obj
//在判断时会调用obj对应对象类的equals()
System.out.println(coll.contains(13)); //true
System.out.println(coll.contains(new String("abc"))); //true 因为String中重写了equals方法
System.out.println(coll.contains(new Person("Tom",12))); //false
// 2. containsAll(coll2) : 判断当前集合是否包含coll2的数据
Collection coll2 = new ArrayList();
coll2.add(13);
coll2.add("str");
System.out.println(coll.containsAll(coll2)); //true
// 3. remove(obj):删除一个相等的对象
System.out.println(coll.remove(13)); //true
System.out.println(coll); //[str, 13.5, abc, Person{age=12, name='Tom'}]
// 4. removeAll(coll2):删除一个相等的集合,求差集
Collection coll3 = new ArrayList();
coll3.add(13.5f);
coll3.add("str2");
coll.removeAll(coll3);
System.out.println(coll); //[str, abc, Person{age=12, name='Tom'}]
}
@Test
public void test03(){
Collection coll = new ArrayList();
coll.add(123); //自动装箱
coll.add(456);
coll.add(13.5f);
coll.add(new String("abc"));
coll.add(new Person("Tom",12));
//1. retainAll() : 取交集
Collection coll2 = new ArrayList();
coll2.add(123);
coll2.add("str");
coll.retainAll(coll2);
System.out.println(coll); // [123]
//2. equals(obj)
//3. hashCode()
System.out.println(coll.hashCode()); //154
//4. 集合 --> 数组 toArray()
Object[] objects = coll.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]); //154 123
}
// 数组--> 集合
List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(strings); //[AA, BB, CC]
List<int[]> ints = Arrays.asList(new int[]{123, 456});
System.out.println(ints); // [[I@32a1bec0]
List<Integer> integers = Arrays.asList(new Integer[]{123, 456});
System.out.println(integers); // [123, 456]
}
}
迭代器的使用
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @Author: mei_ming
* @DateTime: 2022/6/25 19:18
*/
public class IteratorTest {
@Test
public void test01() {
Collection coll = new ArrayList();
coll.add(123); //自动装箱
coll.add(456);
coll.add(13.5f);
coll.add(new String("abc"));
coll.add(new Person("Tom", 12));
// 使用迭代器遍历集合中的元素
Iterator iterator = coll.iterator();
while(iterator.hasNext()){ //判断是否还有下一个元素
//next() 1. 指针下移 2. 将下移后的位置上的元素返回
System.out.println(iterator.next());
}
System.out.println(coll); //[123, 456, 13.5, abc, Person{age=12, name='Tom'}]
iterator = coll.iterator();
//remove() : 删除元素
while(iterator.hasNext()){
//iterator.remove(); //在还未调用next()或调用next()后已经调用了remove方法,再次调用会报IllegalStateException
Object obj = iterator.next();
if("abc".equals(obj)){
iterator.remove();
}
}
System.out.println(coll); //[123, 456, 13.5, Person{age=12, name='Tom'}]
}
//错误示范 1
@Test
public void test02() {
Collection coll = new ArrayList();
coll.add(123); //自动装箱
coll.add(456);
coll.add(13.5f);
coll.add(new String("abc"));
coll.add(new Person("Tom", 12));
// 使用迭代器遍历集合中的元素
Iterator iterator = coll.iterator();
while((iterator.next())!=null){
System.out.println(iterator.next());
}
//结果: 456 abc java.util.NoSuchElementException
}
//错误示范 2
@Test
public void test03() {
Collection coll = new ArrayList();
coll.add(123); //自动装箱
coll.add(456);
coll.add(13.5f);
coll.add(new String("abc"));
coll.add(new Person("Tom", 12));
// 使用迭代器遍历集合中的元素
Iterator iterator = coll.iterator();
while(coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
// 因为集合对象每次调用iterator方法都会得到一个全新的迭代器对象,默认游标都在第一个元素之前
//结果: 循环输出第一个元素
}
}
foreach 增强for
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
/**
* @Author: mei_ming
* @DateTime: 2022/6/25 19:48
*/
public class ForTest {
@Test
public void test01() {
Collection coll = new ArrayList();
coll.add(123); //自动装箱
coll.add(456);
coll.add(13.5f);
coll.add(new String("abc"));
coll.add(new Person("Tom", 12));
//for(集合元素的类型 局部变量 : 集合对象)
//内部仍然是迭代器
for(Object obj:coll){
System.out.println(obj);
}
}
@Test
public void test02() {
int[] arr = new int[]{1,2,3,4,5,6};
//for(集合元素的类型 局部变量 : 集合对象)
for(int i:arr){
System.out.println(i);
}
}
@Test
public void test03() {
String[] arr = new String[]{"MM","MM","MM"};
//方式1: 普通for
for (int i = 0; i <arr.length ; i++) {
arr[i]="GG";
}
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]); //GG GG GG
}
arr = new String[]{"MM","MM","MM"};
//方式2:增强for
for(String str:arr){
str="GG";
}
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]); //MM MM MM
}
}
}
List接口
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Collection 接口: 单列集合,用来存储一个一个得对象
* List接口:存储有序的可重复的数据, -->"动态"数组,替换原有的数组
* ArrayList:作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elementData存储
* LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高,底层使用双向链表存储
* Vector:作为List接口的古老实现类; 线程安全的,效率低,底层使用Object[] elementData存储
*
*
* 1.ArrayList的源码分析:
* 1.1 jdk1.7情况下:
* ArrayList list = new ArrayList(); //底层创建了长度为10的Object[]数组
* list.add(123); //elementData[0]=new Integer(123);
* ...
* list.add(456); //如果此次的添加导致底层elementData数组容量不够,则扩容,
* //默认情况下,扩容为原来的1.5倍,并同时将原有数据拷贝到新的数组中
* 1.2 jdk1.8中ArrayList的变化:
* ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{},并没有创建长度为10的数组
*
* list.add(123); //第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData中
* ...
* 后续的添加和扩容操作与jdk1.7 无异
* 1.3 小结: jdk1.7中的ArrayList的对象的创建类似于单例的饿汉式
* 1.8中的ArrayList的对象的创建类似于单例的懒汉式
*
* 2. LinkedList的源码分析:
* LinkedList list = new LinkedList(); //内部声明了Node类型的first和last属性,默认值为空
* list.add(123); //将123 封装到Node中, 创建了Node对象
*
* 其中,Node定义为: 体现了LinkedList的双向链表
* private static class Node<E> {
* E item;
* Node<E> next;
* Node<E> prev;
*
* Node(Node<E> prev, E element, Node<E> next) {
* this.item = element;
* this.next = next;
* this.prev = prev;
* }
* }
*
* 3. Vector 的源码分析
* jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,
* 在扩容方面,默认为原来的2倍
*
*/
/**
* @Author: mei_ming
* @DateTime: 2022/8/8 21:59
*/
public class ListTest {
//1. ArrayList 中常用方法:
/**
* 增: add(int index,Object ele)
* 删: remove(int index) | remove(Object obj)
* 改: set(int index,Object ele)
* 查: get(int index)
* 长度: size()
* 遍历: 1.Iterator:迭代器
* 2.foreach
* 3.for
*/
@Test
public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(456);
list.add(new Person("Tom",12));
System.out.println(list); //[123, 456, AA, 456, Person{age=12, name='Tom'}]
//1. void add(int index,Object ele): 在index处插入ele
list.add(1,"BB");
System.out.println(list); //[123, BB, 456, AA, 456, Person{age=12, name='Tom'}]
//2. boolean addAll(int index,Collection eles) :从index位置插入eles中的所有元素
List list1 = Arrays.asList(1, 2, 3, 4);
list.addAll(1,list1);
System.out.println(list); //[123, 1, 2, 3, 4, BB, 456, AA, 456, Person{age=12, name='Tom'}]
//3. Object get(int index) : 获取指定index位置的元素
System.out.println(list.get(0)); //123
//4. int indexOf(Object obj): 获取obj在集合中首次出现的位置
int index = list.indexOf(456);
System.out.println(index); // 6
//5. int lastIndexOf(Object obj) : 返回obj在当前集合中末次出现的位置
int index1 = list.lastIndexOf(456);
System.out.println(index1); // 8
//6. Object remove(int index) : 指定移除index位置的元素,并返回此元素
Object remove = list.remove(0);
System.out.println(remove); //123
//7. Object set(int index,Object ele) : 设置指定位置的元素为ele
list.set(0,789);
System.out.println(list); // [789, 2, 3, 4, BB, 456, AA, 456, Person{age=12, name='Tom'}]
//8. List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
List subList = list.subList(5, 8);
System.out.println(subList); //[456, AA, 456]
}
// 遍历List
@Test
public void test2(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
//1. 迭代器
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//2. foreach
for (Object obj: list) {
System.out.println(obj);
}
//3. for
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i));
}
}
// 区分list中remove(int index) 和 remove(Object obj)
@Test
public void test3(){
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
updatelist(list);
System.out.println(list); // 默认按照索引删 结果为 [1, 2]
}
private void updatelist(List list) {
list.remove(2); //[1, 2]
// list.remove(new Integer(2)); //[1, 3]
}
}
Set接口
/**
* Set接口的框架:
* Collection接口:单列集合, 用来存储一个一个的对象
* Set接口:存储无序的,不可重复的数据,
* HashSet: 作为Set接口的主要实现类,线程不安全的,可以存储null值
* LinkedHashSet: 作为HashSet的子类: 遍历器内部数据时,可以按照添加的顺序遍历
* 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
* TreeSet: 可以按照添加对象的指定属性,进行排序
*
* 1. Set接口中没有额外定义的新方法,使用的都是Collection中声明过的方法。
* 2. 要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
*/
import org.junit.Test;
import java.util.*;
/**
* @Author: mei_ming
* @DateTime: 2022/8/14 14:41
*/
public class SetTest {
/**
* 一、Set的无序性和不可重复性:
* 1. 无序性: 不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的HashCode来分配空间
* 2. 不可重复性: 保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个
*
* 二、 添加元素的过程:以HashSet为例:
* 向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
* 此哈希值接着通过某种算法计算出HashSet底层数组中的存放位置(即为:索引位置),判断
* 数组此位置上是否已经有元素:
* 如果此位置上没有其他元素,则 元素a添加成功。 --> 情况1
* 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
* 如果hash值不相同,则元素a添加成功。 --> 情况2
* 如果hash值相同,进而需要调用元素a所在类的equals()方法
* equals()返回true,元素a添加失败
* equals()返回false,则元素a添加成功 -->情况3
*
* 对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储:
* jdk1.7: 元素a 存放在数组中 指向原来的元素
* jdk1.8: 原来的元素存放在数组中,指向元素a
*
*
* HashSet底层: 数组+链表
*/
/**
* 以IDEA为例,在自定义类中可以调用工具自动重写equals和hashCode,
* 问题:为什么复写hashCode方法,有31这个数字,
* 1.选择系数的时候,要选择尽量大的系数,因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高
* 2. 并且31 只占用5bits,相乘造成溢出的概率小。
* 3. 31 可以通过位运算优化, i*31==(i<<5)-1
* 4. 31 是一个素数
*/
/**
* 重写hashCode()方法的基本原则:
* 1. 在程序运行是,同一个对象多次调用hashCode()方法应该返回相同的值。
* 2. 当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法的返回值也应相等
* 3. 对象中用作equals()方法比较的Field,都应该用来计算hashCode值
*/
@Test
public void test1(){
Set set = new HashSet();
set.add(123);
set.add(456);
set.add("AA");
set.add(new Person("Tom",24));
set.add(new Person("Tom",24));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
/**
* 打印结果:
* AA
* Person{age=24, name='Tom'}
* Person{age=24, name='Tom'} //因为Person没有重写equals和hashCode方法
* 456
* 123
*/
}
/**
* LinkedHashSet的作用
* LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个shuju
* 和后一个数据。
* 优点: 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
*/
@Test
public void test2(){
Set set = new LinkedHashSet();
set.add(123);
set.add(456);
set.add("AA");
set.add(new Person("Tom",24));
set.add(new Person("Tom",24));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
/**
* 打印结果:
* 123
* 456
* AA
* Person{age=24, name='Tom'}
* Person{age=24, name='Tom'}
*/
}
/**
* TreeSet:
* 1.向TreeSet中添加的数据,要求是相同类的对象
* 2.两种排序方式 自然排序(实现Comparable接口) 和 定制排序(Comparator)
*
* 3.自然排序中,比较两个对象是否相同的标准为CompareTo()返回0,不再是equals()
* 4.定制排序中,比较两个对象是否相同的标准为Compare()返回0,不再是equals()
*/
@Test
public void test3(){
Set set = new TreeSet();
set.add(123);
set.add(456);
set.add(-11);
set.add(22);
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
/**
* 打印结果:
* -11
* 22
* 123
* 456
*/
Set set1 = new TreeSet();
set1.add(new Person("Tom",12));
set1.add(new Person("Jack",14));
set1.add(new Person("Mike",8));
set1.add(new Person("Mike",16));
Iterator iterator1 = set1.iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}
/**
* 打印结果: 没加 二级排序之前 , 把 {Mike,8} 和{Mike,16}看成是一个对象
* Person{age=12, name='Tom'}
* Person{age=8, name='Mike'}
* Person{age=14, name='Jack'}
*
* 加 二级排序之后 , 把 {Mike,8} 和 {Mike,16}看成是两个对象
* Person{age=12, name='Tom'}
* Person{age=8, name='Mike'}
* Person{age=16, name='Mike'}
* Person{age=14, name='Jack'}
*/
}
@Test
public void test4() {
Comparator com = new Comparator() {
//按照年龄从小到大排序
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o2 instanceof Person){
Person person1 = (Person) o1;
Person person2 = (Person) o2;
return Integer.compare(person1.getAge(),person2.getAge());
}else{
throw new RuntimeException("数据类型不匹配");
}
}
};
Set set1 = new TreeSet(com);
set1.add(new Person("Tom",12));
set1.add(new Person("Jack",14));
set1.add(new Person("Mike",8));
set1.add(new Person("Mike",16));
Iterator iterator1 = set1.iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}
/**
* 打印结果:
* Person{age=8, name='Mike'}
* Person{age=12, name='Tom'}
* Person{age=14, name='Jack'}
* Person{age=16, name='Mike'}
*/
}
// 面试题
@Test
public void test5(){
HashSet set = new HashSet();
//User 已重写 hashCode、equals
User u1 = new User(1001,"AA");
User u2 = new User(1002,"BB");
set.add(u1);
set.add(u2);
System.out.println(set); //2个 [User{id=1002, name='BB'}, User{id=1001, name='AA'}]
u1.name= "CC";
set.remove(u1); // remove 先拿name id 进行hashcode判断, 结果 没有命中 remove的位置是空的
System.out.println(set); //2个 [User{id=1002, name='BB'}, User{id=1001, name='CC'}]
set.add(new User(1001,"CC")); // add 拿 cc 与 1001 进行hashcode 之前的 cc地址是根据aa生成的 所以 add 成功
System.out.println(set); //3个 [User{id=1002, name='BB'}, User{id=1001, name='CC'}, User{id=1001, name='CC'}]
set.add(new User(1001,"AA")); // 这个hashcode 与之前 1001 aa 的相同, 进行equals判断 cc与aa 不同, add 成功
System.out.println(set); //4个 [User{id=1002, name='BB'}, User{id=1001, name='CC'}, User{id=1001, name='CC'}, User{id=1001, name='AA'}]
}
}
Person
/**
* @Author: mei_ming
* @DateTime: 2022/6/25 17:55
* @Description: TODO
*/
public class Person implements Comparable{
int age;
String name;
public Person() {
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Person( String name,int age) {
this.age = age;
this.name = name;
}
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;
}
//按照姓名从大到小排序,年龄从小到大
@Override
public int compareTo(Object o) {
if (o instanceof Person){
Person person = (Person) o;
return -this.name.compareTo(person.name);
// int compare = -this.name.compareTo(person.name);
// if(compare!=0){
// return compare;
// }else{
// return Integer.compare(this.age,person.age);
// }
}else{
throw new RuntimeException("输出的类型不匹配");
}
}
}
User
/**
* @Author: mei_ming
* @DateTime: 2022/6/25 17:55
* @Description: TODO
*/
public class User{
int id;
String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public User() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (id != user.id) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Map接口
import org.junit.Test;
import java.util.*;
/**
* /---Map:双列数据,存储key-value对的数据
* /---HashMap:作为Map的主要实现类,线程不安全的,效率高,存储null的key和value
* /---LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
* 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
* 对于频繁的遍历操作,此类执行效率高于HashMap
* /---TreeMap:保证按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序
* 底层使用红黑树。
* /---Hashtable:作为古老的实现类:线程安全的,效率低;不能存储null的key和value
* /---Properties:常用来处理配置文件。key和value都是String类型
* HashMap的底层:数组+链表 (jdk7之前)
* 数组+链表+红黑树 (jdk8)
*
* 面试题:
* 1. HashMap的底层实现原理?
* 2. HashMap和HashTable的异同?
* 3. CurrentHashMap 与 Hashtable 的异同?
*
* 二、Map的结构的理解:
* Map中的key:无序的、不可重复的,使用Set存储所有的Key --> key所在的类要重写equals和hashCode
* Map中的value: 无序的、可重复的,使用Collection存储所有的value -->value所在的类要重写equals()
* 一个键值对:key-value构成一个Entry对象
* Map中entry:无序的、不可重复的,使用Set存储所有的entry
*
* 三、HashMap的底层原理: (以jdk7为例)
* HashMap map = new HashMap();
* 在实例化以后,底层创建了长度为16的以为数组Entry[] table.
* map.put(key1,value1):
* 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法(indexFor方法)计算以后,得到Entry数组中的存放位置。
* 如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
* 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据,
* 的哈希值:
* 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功 ---情况2
* 如果key1的哈希值和原来的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)
* 如果equals()返回false:此时key1-value1添加成功。 ---情况3
* 如果equals()返回true:使用value1替换value2
*
* 补充: 关于情况2、3,此时key1-value1和原来的数据以链表的方式存储。
*
* 在不断的添加过程中,会涉及到扩容问题,当超出临界值时且要存放的位置非空,则扩容,
* 默认的扩容方式: 原来的2倍,并将原有数据复制过来
*
* jdk8 相较于jdk1.7底层实现方面的不同:
* 1. new HashMap() : 底层没有创建一个长度为16的数组
* 2. jdk8底层的数组时Node[],而非Entry[]
* 3. 首次调用put()方法,底层创建长度为16的数组
* 4. jdk7底层结构只有:数组+链表。jdk8底层结构:数组+链表+红黑树
* 当数组的某个索引位置上的元素以链表形式存在的数据》8 且当前数组长度>64时,
* 此时此索引位置上的所有数据改为红黑树存储
*
* 面试题:
* 谈谈你对HashMap中put/get方法的认识?如果了解再谈谈HashMap的扩容机制?默认大小是多少?
* 什么是负载因子(填充比)?什么是吞吐临界值(阈值/threshold)? 16*0.75 = 12
*
* DEFAULT_INITIAL_CAPACITY: HashMap的默认容量,16
* MAXIMUM_CAPACITY: HashMap的最大支持容量,2^30
* DEFAULT_LOAD_FACTOR: HashMap的默认加载因子,0.75f
* TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树,8
* UNTREEIFY_THRESHOLD: Bucket中红黑树存储的Node小于该默认值,转化为链表,6
* MIN_TREEIFY_CAPACITY: 桶中的Node被树化时的hash表容量。(当桶中Node的数量大到需要变红黑树
* 时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作,这个MIN_TREEIFY_CAPACITY的值
* 至少是TREEIFY_THRESHOLD的4倍),64
*
* table: 存储元素的数组,总是2的n次幂
* entrySet: 存储具体元素的集
* size: HashMap中存储的键值对的数量,
* modCount: HashMap扩容和结构改变的次数
* threshold: 扩容的临界值,= 容量*填充因子
* loadFactor: 填充因子
*
*
* 四、LinkedHashMap的底层实现原理(了解)
*
* static class Entry<K,V> extends HashMap.Node<K,V> {
* Entry<K,V> before, after;
* Entry(int hash, K key, V value, Node<K,V> next) {
* super(hash, key, value, next);
* }
* }
* 五、Map中定义的方法:
* 添加、删除、修改操作:
* Object put(Object key,Object value): 将指定key-value 添加到(修改)当前map中
* void putAll(Map m): 将m中的所有key-value对存放到当前的map中
* Object remove(Object key): 移除指定的key的key-value对,并返回value
* void clear(): 清空当前map中的所有数据
* 元素查询的操作:
* Object get(Object key): 获取指定key对应的value
* boolean containsKey(Object key): 是否包含指定的key
* boolean containsValue(Object value): 是否包含指定的value
* int size(): 返回map中key-value 对的个数
* boolean isEmpty(): 判断当前map是否为空
* boolean equals(Object obj): 判断当前map和参数对象obj是否相等
* 元视图操作的方法:
* Set keySet(): 返回所有key构成的Set集合
* Collection values(): 返回所有value构成的Collection集合
* Set entrySet(): 返回所有key-value对构成的Set集合
*
*
* 总结:常用方法
* 添加:put
* 删除:remove
* 修改:put
* 查询:get
* 长度:size
* 遍历:keySet()/values()/entrySet()
*
* 六、TreeMap
* 向TreeMap中添加key-value,要求key必须是由一个类创建的对象
* 因为要按照key进行排序: 自然排序、定制排序
*/
/**
* @Author: mei_ming
* @DateTime: 2022/9/18 21:22
*/
public class MapTest {
@Test
public void test(){
Map map = new HashMap();
map.put(null,null); //正常
Map map1 = new Hashtable();
map1.put(null,null); //编译出错 java.lang.NullPointerException
}
@Test
public void test2(){
Map map = new HashMap();
map.put("A",1);
map.put("BDX",2);
map.put("C",3);
System.out.println(map); //{A=1, C=3, BDX=2}
Map map1 = new LinkedHashMap();
map1.put("A",1);
map1.put("BDX",2);
map1.put("C",3);
System.out.println(map1); //{A=1, BDX=2, C=3}
}
@Test
public void test3(){
Map map = new HashMap();
//添加
map.put("AA",22);
map.put(33,"BB");
map.put("44",55);
//修改
map.put("AA",78);
System.out.println(map); // {AA=78, 44=55, 33=BB}
Map map1 = new HashMap();
map1.put(44,"BB");
map1.put("99",55);
map.putAll(map1);
System.out.println(map); // {AA=78, 44=55, 99=55, 33=BB, 44=BB}
//remove(Object key)
Object value = map.remove("99");
System.out.println(value); // 55
System.out.println(map); // {AA=78, 44=55, 33=BB, 44=BB}
//clear()
map.clear();
System.out.println(map.size());// 0
}
@Test
public void test4(){
Map map = new HashMap();
//添加
map.put("AA",22);
map.put(33,"BB");
map.put("44",55);
//Object get(Object key)
System.out.println(map.get(33)); // BB
//boolean containsKey(Object key)
boolean b = map.containsKey("55");
System.out.println(b); //false
boolean b1 = map.containsValue(22);
System.out.println(b1); //true
}
//遍历操作
@Test
public void test5(){
Map map = new HashMap();
//添加
map.put("AA",22);
map.put(33,"BB");
map.put("44",55);
//遍历所有key :keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("--------");
//遍历所有value: values()
Collection values = map.values();
for (Object obj:values) {
System.out.println(obj);
}
System.out.println("--------");
//遍历所有key-value对
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
//自然排序
@Test
public void test6(){
TreeMap map = new TreeMap();
Person p1 = new Person("Tom",23);
Person p2 = new Person("Jack",21);
Person p3 = new Person("Jerry",20);
Person p4 = new Person("Rose",26);
map.put(p1,11);
map.put(p2,22);
map.put(p3,33);
map.put(p4,44);
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Object o = iterator.next();
Map.Entry entry = (Map.Entry)o;
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
//定制排序
@Test
public void test7(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Person && o2 instanceof Person){
Person p1 = (Person)o1;
Person p2 = (Person)o2;
return Integer.compare(p1.getAge(),p2.getAge());
}
throw new RuntimeException("输入的类型不匹配!");
}
});
Person p1 = new Person("Tom",23);
Person p2 = new Person("Jack",21);
Person p3 = new Person("Jerry",20);
Person p4 = new Person("Rose",26);
map.put(p1,11);
map.put(p2,22);
map.put(p3,33);
map.put(p4,44);
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Object o = iterator.next();
Map.Entry entry = (Map.Entry)o;
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
Properties类
Properties是Hashtable的子类,常用来处理配置文件。key和value都是String类型。
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @Author: mei_ming
* @DateTime: 2022/9/20 21:40
* @Description: TODO
*/
public class PropertiesTest {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("jdbc.properties");
pro.load(fis);
String name = pro.getProperty("name");
String password = pro.getProperty("password");
System.out.println(name + " "+ password); // ming 123456
}
}
Collections工具类
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Collections: 操作Collection、Map的工具类
*/
public class CollectionsTest {
/**
* 排序操作:(均为static)
* reverse(List): 反转List中元素的顺序
* shuffle(List): 对List集合元素进行随机排序
* sort(List): 根据元素的自然顺序对指定List集合元素按升序排序
* sort(List,Comparator): 根据指定的Comparator产生的顺序对List集合元素进行排序
* 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对象的所有旧值
*/
@Test
public void test(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add(321);
list.add(-99);
list.add(0);
System.out.println(list); //[123, 456, 321, -99, 0]
//Collections.reverse(list); // sout [0, -99, 321, 456, 123]
//Collections.sort(list); //sout [-99, 0, 123, 321, 456]
//Collections.shuffle(list); //sout [-99, 321, 123, 456, 0] sout [123, -99, 0, 456, 321]
//Collections.swap(list,2,3); //sout [123, 456, -99, 321, 0]
list.add(321);
int frequency = Collections.frequency(list, 321); //2
System.out.println(frequency);
System.out.println(list);
}
@Test
public void test2(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add(321);
list.add(-99);
list.add(0);
//List dest = new ArrayList();
//Collections.copy(dest,list); //报错:java.lang.IndexOutOfBoundsException: Source does not fit in dest
//因为dest的size=0,list的size=5 不一致
//System.out.println(dest);
//正常写法:
List dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest,list);
System.out.println(dest); // [123, 456, 321, -99, 0]
}
@Test
public void test3(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add(321);
list.add(-99);
list.add(0);
List list1 = Collections.synchronizedList(list);
//list1是线程安全的
}
}