1.集合框架
1.1 概念
Java集合框架(Java Collections Framework简称JCF)是为表示和操作集合,而规定的一种统一的标准的体系结构。集合框架包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
集合就是用于存储对象的容器。 只要是对象类型就可以存进集合框架中。集合的长度是可变的。 集合中不可以存储基本数据类型的值
1.2 集合和数组的区别
数组和集合相比,数组的缺点是它长度是固定的,没有办法动态扩展。而集合存储数据时是没有长度限制的,是可以动态扩展的。集合容器因为内部的数据结构不同,有多种不同的容器对象。这些容器对象不断的向上抽取,就形成了集合框架。
2.为什么使用集合?
1)我们原来讲过数组.思考: 数组有缺陷?--定容【一定数组定义好,他们得长度就无法改变.如果需要改变数组得长度,变得很复杂。
2)我们是否可以定义一个长度改变的容器。---当然可以。
3)手撕可变长度的容器
package com.java0415.test;
import java.util.Arrays;
/**
* @author: jh
* @create: 2022/4/15
*/
public class MyArray {
private Object [] arr; //声明一个Object类型的数组
private int size;//表示数组的下标
public MyArray(){ //无参构造函数
this(3);
}
public MyArray(int initSize){ //有参构造函数
if(initSize<0){ //长度不合法
throw new RuntimeException("sorry 数组的长度有误。");
}
arr=new Object[initSize];
}
//把元素o放入数组arr
public void addData(Object o){
//判断你的数组是否已满
if(size>=arr.length){
//扩容--(1)容器的长度变长 (2)把原来容器中的元素复制到新的容器中
Object[] newArr = Arrays.copyOf(arr, size * 2);
arr=newArr;
}
arr[size]=o;
size++;
}
//根据下标获取数组中的元素。
public Object getData(int index){
if(index>=size){
throw new ArrayIndexOutOfBoundsException("下标越界");
}
Object o = arr[index];
return o;
}
}
我们自己可以手写一个可变的容器,那么别人也可以手写可变的容器。
java官网 基于数组 根据不同的数据结构 创建了多个类 而这些类统称 为集合框架。
以后 我们在说集合框架时 就表示多个类。
3.集合的架构
4.List集合
4.1 ArrayList
package com.java0415.test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author: jh
* @create: 2022/4/15
*/
public class Demo1 {
public static void main(String[] args) {
//创建集合对象
List list = new ArrayList();
//添加操作
list.add("java01");
list.add("java02");
list.add(15.5);
list.add(18);
list.add(true);
list.add(new Date());
System.out.println(list);
list.add(2,"hello");
System.out.println(list);
List list2=new ArrayList();
list2.add("a");
list2.add("b");
list.addAll(list2);
System.out.println(list);
//删除操作
list.remove(2);
System.out.println(list);
//list.clear();
System.out.println(list);
//修改操作
list.set(1,"张三");
System.out.println(list);
//查询操作
Object o = list.get(3);
System.out.println(o);
int size = list.size();
System.out.println(size);
boolean f = list.contains("java03");
System.out.println(f);
int index = list.indexOf("张三");
System.out.println(index);
for(int i=0;i<list.size();i++){
Object o1 = list.get(i);
System.out.println(o1);
}
}
}
4.2 LinkedList
链表结构
package com.java0415.test.test1;
import java.util.LinkedList;
/**
* @author: jh
* @create: 2022/4/15 22:06
*/
public class DemoLinkedList {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
//添加操作
linkedList.add("张三");
linkedList.addFirst("李四");
linkedList.addLast("王五");
linkedList.addFirst("徐六");
linkedList.addLast("郭七");
System.out.println(linkedList);
//删除操作
linkedList.removeFirst();
System.out.println(linkedList);
linkedList.remove(2);
System.out.println(linkedList);
//linkedList.removeLast();
//System.out.println(linkedList);
//修改操作
linkedList.set(1,"java11");
System.out.println(linkedList);
//查询操作
int size = linkedList.size();
boolean empty = linkedList.isEmpty();
boolean b = linkedList.contains("java01");
Object o = linkedList.get(1);
Object first = linkedList.getFirst();
System.out.println(first);
Object last = linkedList.getLast();
System.out.println(last);
}
}
分析: LinkedList查询效率低,因为它要一个一个节点的往后找。
5.set集合
5.1 HashSet集合
package com.java0416.test;
import java.util.HashSet;
import java.util.Iterator;
/**
* @author: jh
* @create: 2022/4/18
*/
public class HashSetTest {
public static void main(String[] args) {
//创建HashSet对象
HashSet hashSet = new HashSet();
HashSet hashSet1 = new HashSet(16);//初始容器的大小
//loadFactor:--->0.7f 表示负载因子 当空间使用70%时 要求扩容
HashSet hashSet2 = new HashSet(16,0.7f);
//添加操作
hashSet.add("张三");
hashSet.add("李四");
hashSet.add("王五");
hashSet.add("徐七");
hashSet.add("郭九");
HashSet set2=new HashSet();
set2.add("赵四");
set2.add("曾三");
set2.add("陈二");
hashSet.addAll(set2);
System.out.println(hashSet);
//删除操作
hashSet.remove("曾二");
//hashSet.clear();//清空容器集合
System.out.println(hashSet);
//查询操作,HashSet无修改方法
boolean e = hashSet.isEmpty(); //判断是否为空
System.out.println(e);
boolean b = hashSet.contains("赵四");//判断元素是否在容器中
System.out.println(b);
//hashSet的遍历
//通过for each遍历
for(Object o: hashSet){
System.out.println(o);
}
//通过迭代器来遍历
Iterator iterator = hashSet.iterator();//获取迭代器对象 有序:有下标
while (iterator.hasNext()){//判断是否指定能够移动
Object next = iterator.next();//指定移动并获取当前的元素
System.out.println(next);
}
}
}
5.2 TreeSet集合
TreeSet中的方法和HashSet中的方法一模一样 只是他们的实现不一样。
TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。
存储一个对象类型时,运行会发生错误
发现: TreeSet中的元素必须实现Comparable接口 方可放入TreeSet
解决办法:(两个)
1.让你的类实现Comparable接口
package com.java0416.test;
import java.util.TreeSet;
/**
* @author: jh
* @create: 2022/4/18
*/
public class TreeSetTest {
public static void main(String[] args) {
/*
//存储String类型
TreeSet treeSet=new TreeSet();
treeSet.add("java1");
treeSet.add("java3");
treeSet.add("java5");
treeSet.add("java7");
treeSet.add("java9");
treeSet.add("java4");
System.out.println(treeSet);*/
//存储一个对象类型
TreeSet treeSet=new TreeSet();//TreeSet不允许重复元素
treeSet.add(new Student("张三",20,"男"));
treeSet.add(new Student("赵四",45,"男"));
treeSet.add(new Student("王五",34,"女"));
treeSet.add(new Student("李二",26,"男"));
System.out.println(treeSet);
}
}
class Student implements Comparable{
private String name;
private Integer age;
private String sex;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex+
'}';
}
public Student(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
//排序:---返回如果大于0 表示当前元素比o大 如果返回-1 当前添加的元素比o小 返回0表示相同元素。
@Override
public int compareTo(Object o) {
Student student= (Student) o;
System.out.println(this+"---->"+o);
if(this.age>student.age){
return 1;
}
if(this.age<student.age){
return -1;
}
return 0;
}
}
2.在创建TreeSet时指定排序的对象
我们之前 创建过TreeSet对象。TreeSet treeSet=new TreeSet(); 但是在创建对象时 并没有为其指定排序得规则,那么就要求该集合得元素有排序规则。 如果元素得类已经创建完成,不能修改该类得源码,这时我们又想把该类得对象放入得TreeSet容器中。 这时就需要你在创建TreeSet时指定排序得规则。
//需要比对得两个对象
@Override
public int compare(Object o1, Object o2) {
Student s1= (Student) o1;
Student s2= (Student) o2;
if(s1.getAge()>s2.getAge()){
return 1;
}else if(s1.getAge()<s2.getAge()){
return -1;
}else {
return 0;
}
}
}
public class Demo01 {
public static void main(String[] args) {
//Comparator<? super E> comparator
//为TreeSet容器指定了排序规则
TreeSet treeSet=new TreeSet(new MyComparator());
treeSet.add(new Student(18,"张三"));
treeSet.add(new Student(17,"李四"));
treeSet.add(new Student(19,"王五"));
treeSet.add(new Student(19,"李二"));
System.out.println(treeSet);
}
}
6.Map 属于键值对模式
map中得每个元素属于键值对模式。 如果往map中添加元素时 需要添加key 和 value. 它也属于一个接口,该接口常见得实现类有: HashMap。
package com.java0418.test;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author: jh
* @create: 2022/4/19
*/
public class MapTest {
public static void main(String[] args) {
//创建Map对象
//默认初始化大小为16 负载因子为0.75
Map map = new HashMap();
//初始化大小
Map map2 = new HashMap(16);
//初始化大小 负载因子
Map map3 = new HashMap(16, 0.78f);
//添加操作
map.put("name", "张三"); //注意: 要求map得key必须唯一。
map.put("age", 18);
map.put("name", "李四"); //因为key不能重复,所以后者会把前者覆盖
Map m1 = new HashMap();
m1.put("k1", "v1");
m1.put("k2", "v2");
map.putAll(m1);
map.putIfAbsent("age", 28);//如果指定得key存在,则不放入map中,如果不存在则放入map中
System.out.println(map);
//删除操作
map.remove("age2");//根据指定得key移除元素
System.out.println(map);
//map.clear(); //清空map容器
System.out.println(map);
//修改操作
map.replace("name", "王五");//替换元素
System.out.println(map);
//查询操作
boolean f = m1.containsKey("k3");//判断map是否存在指定得key
System.out.println(f);
Object v = m1.get("k2"); //根据指定的key获取对应得value值
System.out.println(v);
Set keys = m1.keySet();//返回该map中所有得key
System.out.println(keys);
//遍历map.
for (Object k : keys) {
Object value = map.get(k);//
System.out.println(k + "----->" + value);
}
}
}
6.1 HashMap得底层原理
JDK1.7 和 JDK1.8他们是有区别得。
JDK1.7使用得数据结构: 数组+链表 而且链表插入模式为头部插入(造成死循环)。
jdk1.8使用得数据结构: 数组+链表+红黑树 而且链表得插入模式为尾部插入。
从构造函数入口:
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
总结:
JDK1.8 HashMap原理
Hashmap得原理,存储元素使用得put(key,value),根据key得hash计算出相应得哈希值,根据相应得算法求出该元素在数组中得位置, 如果求出得哈希值相同,则称为哈希冲突,会根据equals来判断元素是否一致,如果equals不同,则存入单向链表上, 如果哈希碰撞得个数超过8个,则把链表转换为红黑二叉树。
JDK1.7和JDK1.8 HashMap得区别。
JDK1.7使用得数据结构: 数组+链表 而且链表插入模式为头部插入(造成死循环)。
jdk1.8使用得数据结构: 数组+链表+红黑树 而且链表得插入模式为尾部插入。