为什么使用集合?
![](https://i-blog.csdnimg.cn/blog_migrate/77a712f724ffa99ebec3f2d2dac6365c.png)
List集合
1. 集合: 理解为容器--它的长度可以变大的,而且它存储的都是对象类型。
2. List:----->它是一个接口,该集合中的元素可以重复,而且是有序的(有下标)。
ArrayList--->它的底层是数组结构,它的查询效率高,但是它的添加和删除效率低--因为它要牵涉到数据的迁移。
常见方法: add();size();indexOf(); contains(); get();remove();isEmpty();
LinkedList--->底层双向链表结构,它增加和删除效率高,但是它的查询效率低。
常见的方法: 它的方法和ArrayList中的方法比较相似,它也有自己特有的方法:
addFirst() addLast() getFirst() getLast()
LInkedList:
public class TestLinkedList { public static void main(String[] args) { LinkedList linkedList = new LinkedList(); //添加 linkedList.add("java01"); //追加尾部 linkedList.addFirst("java02"); //添加到头部 linkedList.addLast("java03");//追加到尾部 linkedList.addFirst("java04"); //追加到头部 linkedList.addLast("java05");//追加到尾部 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的底层源码:
1.凡是查询源码 ,我们都是从类的构造方法入手:
/**
* Constructs an empty list.
*/
public LinkedList() {
}
该类的构造方法内是空的,没有任何的代码。 但是该类中有三个属性。
transient int size = 0; //索引
transient Node<E> first; //第一个元素对象
transient Node<E> last; //表示最后一个元素对象。
================ add的源码=====E:理解为Object类型==========================。
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
//上一个节点 数据 下一个节点
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
==================Node的源码 内部类=======================================
private static class Node<E> { //<E>泛型--object
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;
}
}
Set集合
HashSet集合
(1)创建HashSet对象。public class Test02 {
public static void main(String[] args) {
HashSet hashSet= new HashSet();
HashSet hashSet1 = new HashSet(16);//初始容器的大小
//loadFactor:--->0.7f 表示负载因子 当空间使用70%时 要求扩容
HashSet hashSet2 = new HashSet(16,0.7f);
}
}
(2) 添加元素
//添加操作
hashSet.add("java01");
hashSet.add("java02");
hashSet.add("java04");
hashSet.add("java03");
hashSet.add("java02");
HashSet set2=new HashSet();
set2.add("刘德华");
set2.add("张学友");
set2.add("黎明");
hashSet.addAll(set2); //把set2中的每个元素添加到hashset中
System.out.println(hashSet); //元素不能重复 而且无序
(3) 删除
//删除
hashSet.remove("黎明");
// hashSet.clear();//清空容器集合
System.out.println(hashSet);
(4) 修改
//修改操作
boolean empty = hashSet.isEmpty(); //判断是否为空
System.out.println(empty);
boolean b = hashSet.contains("刘德华");//判断元素是否在容器中
System.out.println(b);
(5) hashSet的遍历
①通过foreach遍历
//遍历--- foreach for(Object o: hashSet){ System.out.println(o); }
②通过迭代器来遍历
//迭代器遍历 Iterator iterator = hashSet.iterator();//获取迭代器对象 有序:有下标 while (iterator.hasNext()){//判断是否指定能够移动 Object next = iterator.next();//指定移动并获取当前的元素 System.out.println(next); }
(6) hashSet的源码
从构造函数说起: /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); } 在创建一个HashSet的对象时,底层创建的是HashMap。我们说hashset的底层原理时,我们就在后HashMap的原理就行。 讲HashMap时给大家说原理。
TreeSet集合。
TreeSet中的方法和HashSet中的方法一模一样 只是他们的实现不一样。 TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。
例子: 存储String类型。
TreeSet treeSet=new TreeSet(); treeSet.add("java05"); treeSet.add("java03"); treeSet.add("java04"); treeSet.add("java01"); treeSet.add("java02"); treeSet.add("java04"); System.out.println(treeSet);
存储一个对象类型:
public class Test04 { public static void main(String[] args) { TreeSet treeSet=new TreeSet(); treeSet.add(new Student("王俊凯",17)); treeSet.add(new Student("赵晓普",16)); treeSet.add(new Student("赵俊涛",16)); treeSet.add(new Student("闫克起",15)); System.out.println(treeSet); } }
通过运行我们发现出现如下的错误:
发现: TreeSet中的元素必须实现Comparable接口 方可放入TreeSet
解决办法有两个:
第一个: 让你的类实现Comparable接口
package com.ykq; import java.util.TreeSet; /** * @program: day03 * @description: * @author: 闫克起2 * @create: 2022-04-16 15:59 **/ public class Test04 { public static void main(String[] args) { TreeSet treeSet=new TreeSet(); //TreeSet不允许重复元素 treeSet.add(new Student("王俊凯",17)); treeSet.add(new Student("赵晓普",16)); treeSet.add(new Student("赵俊涛",16)); treeSet.add(new Student("闫克起",15)); System.out.println(treeSet); } } class Student implements Comparable{ private String name; private Integer age; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } public Student(String name, Integer age) { this.name = name; this.age = age; } 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; } //排序:---返回如果大于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; } }
第一个:在创建TreeSet时 为其指定排序的规则
我们之前 创建过TreeSet对象。
TreeSet treeSet=new TreeSet(); 但是在创建对象时 并没有为其指定排序得规则,那么就要求该集合得元素有排序规则。 如果元素得类已经创建完成,不能修改该类得源码,这时我们又想把该类得对象放入得TreeSet容器中。 这时就需要你在创建TreeSet时指定排序得规则。
public class MyComparator implements Comparator {
//需要比对得两个对象
@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);
}
}
Map 属于键值对模式
map中得每个元素属于键值对模式。 如果往map中添加元素时 需要添加key 和 value. 它也属于一个接口,该接口常见得实现类有: HashMap.
(1) 如何创建Map对象
//默认初始化大小为16 负载因子为0.75 Map map=new HashMap(); //初始化大小 Map map2=new HashMap(16); //初始化大小 负载因子 Map map3=new HashMap(16,0.78f);
(2) 添加操作
//默认初始化大小为16 负载因子为0.75 Map map=new HashMap(); //添加操作 key: name value: 高景霞 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); //把m1中得每个元素 添加到map中 map.putIfAbsent("age",28) ;//如果指定得key存在,则不放入map中,如果不存在则放入map中 System.out.println(map);
(3) 删除操作
//删除操作 map.remove("age2");//根据指定得key移除元素 System.out.println(map); map.clear(); //清空map容器 System.out.println(map);
(4) 修改操作
//修改操作 map.replace("name","刘德华");//替换元素 System.out.println(map);
(5) 查询
public static void main(String[] args) { Map map=new HashMap(); map.put("k1","v1"); map.put("k4","v4"); map.put("k2","v2"); map.put("k3","v3"); //查询操作 boolean f = map.containsKey("k5");//判断map是否存在指定得key Object v = map.get("k5"); //根据指定的key获取对应得value值 System.out.println(v); Set keys = map.keySet();//返回该map中所有得key System.out.println(keys); //遍历map. for(Object k:keys){ Object value= map.get(k);// System.out.println(k+"================>"+value); } }
(6) 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使用得数据结构: 数组+链表+红黑树 而且链表得插入模式为尾部插入。