集合
1、内存层面需要针对多个数据进行存储,此时可以考虑的容器有 :数组、集合类
2、数组存储多个数据方面的特点
数组一旦初始化,其长度就是固定的
数组中多个元素是依次紧密排列的 有序的可重复的
(优点)数组一旦初始化完成,其元素类型就是确定的,不是此类型的元素,就不能添加到数组中(集合本身不会检查元素类型,检查元素是否一致是泛型的工作)
元素中类型可以是基本数据类型 也可以是引用数据类型 (相较于集合 集合只能存放引用数据类型,基本数据类型的数据会进行自动装箱存库)
数组存储数据方面的弊端:
数组长度一旦初始化 其长度就不可变了
数组存储数据具有单一性,对于无序的、不可重复的场景的多个数据就无能为力了。
数组中可用的方法属性就极少,具体的需求都需要组织相关代码逻辑
针对数组中插入删除操作,性能较差
3、Java集合框架:java.util
java.util.Collection:存储一个一个的数据
|----子接口:List:有序的可以重复的数据("动态“数组)
|----ArrayList(主要实现类)、LinkedList、Vector
|----子接口:Set:无序的不可重复的数据
|----: HashSet/LinkedHashSet/TreeSet
Java.util.Map:存储一对一对的数据
无子接口
|---- HashMap/LinkedHashMap/TreeMap、Hashtable/Properties
collection接口方法的使用
add:添加元素到当前集合
addAll:添加其他集合中所有元素到当前的集合中
retailAll(Collection coll) 从当前集合中删除两个集合中不相同的元素 使得当前集合仅保留相同的元素
toArray:返回包含当前集合中所有元素的数组
数组转换集合:Arrays.asList(arr)
hashcode:获取当前集合的哈希值
iterator:返回迭代器对象 用于集合遍历
迭代器:Iterator
作用:用来遍历集合中元素
@Test
public void test01(){
Collection coll =new ArrayList();
coll.add("asd");
coll.add(123);
coll.add(0);
coll.add("we");
//获取迭代器的对象
Iterator iterator = coll.iterator();
//通过iterator的hasNext 和 Next实现遍历 而不是用for循环
while (iterator.hasNext()){
System.out.println(iterator.next()); //next:指针下移 将下移之后的值返回
}
}
增强for循环
作用用来遍历集合 数组
针对集合来讲 foreach 底层使用的是迭代器
增强for循环执行过程中,是将集合或者数组中的元素依次赋值给临时变量,在循环体中对临时变量进行修改时,可能不会导致原有集合中元素进行修改
List实现的特点:
java.util.Collection:存储一个一个的数据
|----子接口:List:有序的可以重复的数据("动态“数组)
|----ArrayList主要实现类:线程不安全 效率高、底层使用的Object[]类型的数组存储 查询o1 删除、增加 on 查找效率高 删除效率低
|----LinkedList:底层使用双向链表存储 删除增加 o1 查询 on
|----Vector:古老的实现类 线程安全、效率低、底层使用的Object[]类型的数组
|----子接口:Set:无序的不可重复的数据 类似与数学中的集合 相较于list和map 使用场景较少
|----: HashSet:主要实现类;底层使用的是HashMap,数组链表红黑树
|----LinkedHashSet:是HashSet的子类 数组链表红黑树的基础上添加了双向链表,可以用于添加元素的先后顺序实现遍历,便于频繁的查询操作
无序性 :不等于随机性,添加元素顺序和遍历元素的顺序不一致也不是无序性,无序性和添加元素的位置有关。
这里是根据添加元素的哈希值,计算其在数组中的位置。此位置不是依次排列的表现为无序性
不可重复性:不可重复的标准时,需要判断哈希值,hashCode以及equals的到的结果
添加到set的对象需要重写equals 和 hashCode
|----TreeSet:底层使用红黑树存储,可以按照添加元素的的指定属性的大小进行遍历。
使用Tree 底层是红黑树 可以实现排序输出。
特点:可以按照添加元素的指定属性的大小顺序进行排序遍历
要求:要求添加到TreeSet是同一个类型的元素
判断数据相同的标准:不再考虑HashCode和equals方法,也就意味着TreeSet中元素所在的类不需要重写这两个方法
比较元素是否相等,是考虑自然排序或者定制排序中compare和compareTo
如果compare或者compareto的返回值为0,则认为两个对象相等。由于TreeSet中不能存放相同的元素,则后一个相等的元素就不能添加到TreeSet中
Map及其实现类对比:Map存储一对一对的数据
HashMap:主要实现类 线程不安全的 执行效率高 可以添加null的key和value 底层使用数组+单项链表+红黑树(jdk8)
LinkedHashMap:HashMap的子类:在HashMap使用数据结构的基础上,增加一对双项链表,用于记录添加的先后顺序,进而在遍历的时候按照添加元素进行遍历显示
对于频繁的遍历操作 建议使用LinkedHashMap
TreeMap: 底层就是红黑树,可以按照添加元素Key-value中Key元素指定属性的大小进行遍历 需要考虑使用自然排序和定制排序
Hashtable:古老实现类 线程安全的 但是执行效率不高 有synchronized修饰 底层使用数组+单项链表
Properties: Hashtable的子类 存储的键值对都是String类型 常用来处理属性文件
HashMap中元素的特点:
HashMap中所有的Key彼此之间是不可重复的、无序的。所有的Key构成一个Set--->key所在的类需要重写hashCode()和equals()
HashMap中所有的value彼此之间可重复,无序的。 所有value构成一个Collection集合,value所在的类需要重写equals方法
HashMap中一个key-value就构成了一个不可重复的无序的。所有的entry就构成一个Set集合。
TreeMap的使用
底层使用红黑树存储
可以按照添加的key-value中的key元素的属性的大小进行遍历
需要考虑使用自然排序、定制排序
要求 key需要是同一个类型的对象
Collections工具类
参考数组操作的工具类Arrays,Collections是一个操作Set、List、Map等集合的工具类
collection和Colletions的区别
Collection:是集合框架中用于存储一个一个元素的接口,分为List Set等子接口
Collections:用于操作集合框架的一个工具类。此时的集合框架包括Set、List、Map
常用方法:
排序:
reverse(list):反转集合中的元素
shuffle(list):对list集合元素随机进行排列
sort(List)排序
sort(List.compare)根据指定元素排序
swap(List,int,int) 交换list中指定索引位置的元素
查找:
max(collection):根据元素的自然顺序,返回给定元素的最大元素
min:最小
binarySearch(List list ,T key):在List中查找某个元素的下标,但是List的元素必须是T或者T的子类,元素必须有序 此时使用的二分法查找
frequency(Collection coll ,Object o) :判断某元素在集合中出现的次数。
复制、替换:
copy:复制
replaceAll(List list,Object old ,Object new )
同步:
synchronizedXxx()方法 该方法可以使指定集合包装成线程安全的集合
集合源码分析
数据的逻辑结构
集合结构
线性结构 一对一
树形结构 一对多
图形结构 多对多、
物理结构
顺序结构
链式结构
索引结构
散列结构
开发中我们更习惯如下方式理解下列结构
线性表 :一对一关系:一堆数组、单向链表,双向链表、栈、队列
树:二叉树、B+树、
图:多对多的关系
哈希表:HashMap/HashSet
相关算法和操作
分配资源、建立结构、释放资源
插入和删除
获取和遍历
修改和排序
常见数据结构
3、数组:
4、链表:
链表中基本单位是是节点:Node
4.1单向链表:
Class Node{
Object date;
Node next;
public Node (Object data){
this.data=data;
}
}
创建对象
Node node1=new Node("aa");
Node node2 =new Node("bb");
node1.next=node2;
4.2双向链表:
class Node{
Node prev;
Object data;
Node next;
public Node(Object data){
this.data=data;
}
public Node(Node prev ,Object data,Node next){
this.prev =prev;
this.data=data;
this.next=next;
}
}
创建对象
Node node1=new Node(null,"AA",null);
Node node2=new Node(node1,"BB",null);
Node node3=new Node(node2,"cc",null);
node1.next=node2;
node2.next=node3;
5、二叉树
class TreeNode{
TreeNode left;
Object data;
TreeNode right;
public TreeNode(Object data){
this.data=data;
}
public TreeNode(TreeNode left, Object data,TreeNode right){
this.left=left;
this.data=data;
this.right=right;
}
}
创建TreeNode
TreeNode node1=new TreeNode(null ,"AA",null);
TreeNode leftNode=new TreeNode(null ,"BB",null);
TreeNode rightNode=new TreeNode(null ,"CC",null);
node1.left=leftNode;
node1.right=rightNode
6、栈
stack 先进后出
属于抽象数据类型(ADT)
可以使用数组或者链表构建
class Stack{
Object[] values;
int size;//记录存储元素的个数
public Stack(int length){
values=new Object[length];
}
//入栈
public void push(Object ele){
if(size>=values.length){
throws new RunTRimeException("栈空间已满,入栈失败");
}
valuse[size] = ele;
size++;
}
//出栈
public Object pop(){
if(size<0){
throws new RunTRimeException("栈空间已空,入栈失败");
}
Object obj= values[size-1];
values[size-1]=null;
size--;
return obj;
}
}
7、队列
queue
先进先出
属于抽象数据类型(ATD)
数组实现队列
class Queue{
Object[] values;
int size;
public Queue (int length){
values=new Object[length];
}
public void add(Object ele){
//添加
if(size>=values.length){
throws new RunTRimeException("队列已满,入队失败");
}
values[size]=ele;
size++;
}
public Object get(){
//获取
if(size>=values.length){
throws new RunTRimeException("队列已空,出队失败");
}
Object obj=values[0];
//数据前移
for(int i ;i
List源码解析
ArrayList源码解析
jdk7:
底层会初始化数组,长度为10 Object[] elementData=new Object[10]{};
ArrayList list=new ArrayList<>();
list.add("aa");//elementData[0]="aa";
list.add("bb");//elementData[1]="bb";
当要添加11个元素时,elementData数组已满,则需要扩充。默认扩充为原来长度的1.5倍。并将原来的数组中的元素复制到新的数组中
当又不够时 可以扩容到与原来的1.5倍
jdk8:
底层会初始化数组,长度为10 Object[] elementData=new Object[]{};
ArrayList list=new ArrayList<>();
list.add("aa");//首次添加元素时会重新初始化数组Object[] elementData=new Object[10]; elementData[0]="aa
list.add("bb");//elementData[1]="bb";
当要添加11个元素时,elementData数组已满,则需要扩充。默认扩充为原来长度的1.5倍。并将原来的数组中的元素复制到新的数组中
当又不够时 可以扩容到与原来的1.5倍
小结:
jdk1.7一上来 ArrayList类似与饿汉式
jdk1.8一上来 ArrayList类似与懒汉式
Vector
线程安全,存储有序可以重复的数据
jdk1.8
Vector v=new Vector();//底层初始化数组 长度10;Object[] elementData=new Object[10];
v.add("aa");//elementData[0]="aa";
v.add("bb");//elementData[1]="bb";
当添加11个元素时,需要扩容,扩容为原来的1.5倍
LinkedList:双向链表
LinkedList list=new LinkedList<>();//底层没有做什么
list.add("aa");//将"aa"封装到Node对象中,list对象的属性first、last都指向Node对象
list.add("bb");//将"bb"封装到Node对象中,对象1对象2构成双向链表 对象1 last指向Node对象2
LinkedList使用的是双向链表 不需要扩容
LinkesHashMap
LinkedHashMap与HashMap的关系:
LinkedHashMap在HashMap使用数组链表红黑树的基础上,又增加了一对双向链表,记录添加的Key,value的先后顺序。便于我们遍历我们的(key,value)
底层结构:LinkedHashMap内部定义了一个entry entry继承自HashMap的Node 底层增加了一对双向链表
HashSet和LinkedHashSet的源码分析
底层使用的是HashMap
LinkedHashSet底层使用的是LinkedHashMap、
HashMap源码
JDK7:
创建对象的过程中,底层会初始化Entry[] table=new Entry[16];
HashMap map =new HashMap<>();
map.put("aa",78);//"aa"和78都封装到一个Entry对象中,考虑将此对象添加到table数组中。
添加或者修改的的过程:
将(key1,value1)添加到当前map中:
首先调用key1所在类的hashcode方法,计算key1对应的hash1,此哈希值1经过某种算法(hash())之后得到哈希值2.
哈希值2再通过某种算法(indexFor())之后,就确定了(key1,value1)其在数组table中索引位置i;
1.1如果此索引位置i的数组上没有元素,则(key1,value1)添加成功 --->情况1
1.2如果此索引位置上有元素(key2,value2) 则需要继续比较key1和key2的哈希值2。 --->哈希冲突
2.1如果key1的哈希值2 和 key2的哈希值2不同,则(key1,value1)添加成功。--->情况2
2.2如果key1的哈希值2和 key2的哈希值2相同,则需要继续比较key1和key2 equals().需要调用key1所在类的equals方法,将key2作为参数传递进去。
3.1 调用equals返回的是false:则(key1,value1)添加成功。--->情况3
3.2 调用equals返回的是true:则认为key1和key2是相同的,默认情况下,value1替换原有的value2;
补充说明:
情况1:将(key1,value1)存放到数组i的位置;
情况2情况3:将(key1,value1)与现有的(key2,value2)构成单向链表结构,(key1,vlaue1)指向(key2,value2);
随着不断添加元素,满足如下条件的情况下会考虑扩容:
(size>=threshold)&&null!=table[i])
当元素的个数大于临界值(数组长度*加载因子)时,就考虑扩容;默认的临界值=16*0.75 --->12
默认扩容为原来的两倍
JDK8:
8和7的不同之处:
1、在jdk8中当我们创建了HashMap实例之后,底层并没有初始化table数组。当首次添加(key,value)时进行判断,如果发现table尚未初始化,则需要对数组进行初始化。
2、在jdk8中底层定义了Node内部类,替换了jdk7中的Entry内部类,意味着我们创建的数组是Node[] 叫法不一致 底层一样 都实现了Entry接口
3、在jdk8中,如果当前的(key,value)经过一系列的判断之后可以添加到当前的数组交表i中,如果此时角标i有元素 ,在jdk7是将新的(key,value)指向旧的元素,而在jdk8中是旧元素指向新元素 尾插法 七上八下
4、jdk7中数组加链表
jdk8 数组链表 加红黑树
什么时候单项链表会变成红黑树: 如果数组索引位置i上的元素达到8;并且数组长度达到64,我们会将此索引位置i的多个元素改为使用红黑树。
为什么修改为红黑树 :红黑树进行put、get、remove操作时间复杂度为o(logn),比单项链表时间复杂度o(n)好
什么时候红黑树变成单项链表:当使用红黑树索引i位置的元素个数低于6的时候就会将红黑树结构退化成单项链表。