一 java中的容器
1、容器(Container)定义
1.1 web容器
容器可以管理对象的生命周期、对象与对象之间的依赖关系,您可以使用一个配置文件(通常是XML),在上面定义好对象的名称、如何产生(Prototype 方式或Singleton 方式)、哪个对象产生之后必须设定成为某个对象的属性等,在启动容器之后,所有的对象都可以直接取用,不用编写任何一行程序代码来产生对象,或是建立对象与对象之间的依赖关系。 换个更直白点的说明方式:容器是一个Java 所编写的程序,原先必须自行编写程序以管理对象关系,现在容器都会自动帮您作好。
常用web容器:WebSphere,WebLogic,Resin,Tomcat
Spring也是一个容器,对对象进行管理。
1.2 容器类
用来存放其他类产生的对象的类,这个类,就是容器,或就叫它集合==》若干性质相近或相同的对象存放的类。通俗说法:提供一个放程序或数据等的地方;容器本身是一个组件与其他组件的区别是——他可以存放其他的组件,也就是说其他的组件可以放在他的上面。
Java容器类包含List、ArrayList、Vector及map、HashTable、HashMap、Hashset
ArrayList和HashMap是异步的,Vector和HashTable是同步的,所以Vector和HashTable是线程安全的,而 ArrayList和HashMap并不是线程安全的。因为同步需要花费机器时间,所以Vector和HashTable的执行效率要低于 ArrayList和HashMap。
上述定义参看百度百科。
1.3 数组与集合的区别
- 数组长度声明即固定,有序,存储同一类型对象。
- 集合使程序变得更加灵活与高效。
- 容器不是数组,不能通过下标的方式访问容器中的元素 。
二、数组
1 定义
(1)引用数据类型;(2)数组实际上是一个容器,“装(存储)数据”,数组类型相同,而且是有序的;(3)数组是一次性声明多个相同变量的变量,变量的名称都相同,使用序号来访问。
(2)
2 底层原理
栈内存中存储数据:基本数据类型的值与引用数据类型的内存地址;
基本数据类型,在进行方法调用时,传递的是基本数据类型的值的副本;
引用数据类型,在进行方法调用时,传递的是引用数据类型的内存地址的副本(引用)。
3 声明方式
//【1】数组的声明方式,这种方式,只声明,是不能使用的,想要使用必须到堆里开空间,必须new... int [] array; //建议使用这种 int arrayB[]; //【2】声明幷分配空间 String []str = new String[20]; //因为String是引用数据类型,所以默认值都是null double price[]=new double[4]; //默认值为 0.0 //【3】声明幷赋值(数组的静态赋值,在程序运行前,值就已确定) int[]arrayA={12,23,4,53,56}; int [] arrayC=new int[]{34,43,23};
4 简单算法
4.1 冒泡排序
package com.asd.reserve.utils.collections; /** * @author zs * @date 2020/1/7 15:47 */ public class PaiXu { //主方法用于测试 public static void main(String [] args){ int [] arr={43,34,4,44,5,42}; //调用冒泡排序的方法 PaiXu.maoPao(arr);//arr是实际参数 //调用输出的方法 PaiXu.print(arr); System.out.println("\n\n"); int [] arrB={43,56,4,56,7,77,75,645}; //调用冒泡排序的方法 PaiXu.maoPao(arrB);//arr是实际参数 //调用输出的方法 PaiXu.print(arrB); } /**该方法的功能,是进行排序*/ public static void maoPao(int [] arr){ for(int i=0;i<arr.length-1;i++){ //外层N-1 for(int j=0;j<arr.length-1-i;j++){//内层N-1-i //两两相比 if(arr[j]>arr[j+1]){ //[j]>[j+1] //交换 int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } }//内for end }//外for end } public static void print(int []arr){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"\t"); } } }
内层控制的是比较方式:冒泡排序每次比较都是相邻的两个比较。
4.2选择排序
public class PaiXu{ //主方法用于测试 public static void main(String [] args){ int [] arrB={43,56,4,57,7}; //调用选择排序的方法 PaiXu.xuanZe(arrB);//arr是实际参数 //调用输出的方法 PaiXu.print(arrB); /*int x=4,y=9; System.out.println("交换前:x="+x+",y="+y); change(x,y); System.out.println("交换后:x="+x+",y="+y);*/ } /**该方法的功能,是进行排序*/ public static void maoPao(int [] arr){ for(int i=0;i<arr.length-1;i++){ //外层N-1 for(int j=0;j<arr.length-1-i;j++){//内层N-1-i //两两相比 if(arr[j]>arr[j+1]){ //[j]>[j+1] //调用交换的方法 change(arr,j,j+1); } }//内for end }//外for end } /**该方法的功能,是排序, 选择排序*/ public static void xuanZe(int [] arr){ for(int i=0;i<arr.length-1;i++){ //比较的轮数 for(int j=i+1;j<arr.length;j++){ //比较的次数 if(arr[i]>arr[j]){ //调用交换的方法 change(arr,i,j); } } } } /**交换的方法*/ public static void change(int [] arr,int a,int b){ //arr是数组,a与b,是数组中元素的下标 int temp=arr[a]; //整个这个交换是交换的堆里的数据 arr[a]=arr[b]; arr[b]=temp; } public static void print(int []arr){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"\t"); } }
4.3递归
一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的。
参看百度百科。
package com.luna.base; public class Plus { //1加到100 public int sum(int i) { if (i == 1) { return 1; } return i + sum(i - 1); } public static void main(String[] args) { Plus plus = new Plus(); System.out.println("计算结果:" + plus.sum(100) + "!"); }
参看链接:https://blog.csdn.net/u011635492/article/details/80715832
5 二维数组
第一种声明方式
int [] arrA={12,32,34,5}; //一维数组,arrA是引用数据类型 int [] arrC={32,45}; int [] arrD={43,45,43,46,78,9}; //声明一个二维数组,用于存储3个一维数组,每个一维数组的长度可以不相同 int [] [] array=new int [3][]; //赋值--》赋的内存地址, 存储的是一维数组内存地址 array[0]=arrA; array[1]=arrC; array[2]=arrD; for(int i=0;i for(int j=0;j System.out.print(array[i][j]+"\t"); } System.out.println("\n=================================="); }
第二种声明方式
int [] ar[]={{1223,32,32},{4,32,3},{43,43,4},{3,43,45}};
第三种声明方法
//声明一个长度为4的二维数组,用于存储4个一维数组,每个一维数组长度是2,默认为0 int a[][]=new int[4][2];
三、集合
1 定义
容器,存储数据。主要是放对象,当前台访问,将数据封装到一个对象,而对象可能是多个,在与数据库进行交互时,一般都用集合来存放数据。它的长度可变。
2 与数组比较
- 数组一旦声明,其长度固定,类型一致,不够灵活;
- 数组的插入,删除效率低。
3 分类
3.1 Collection:
总的接口:无序且不唯一;
3.1.1 两个子接口:List和Set。还有一个不常用的Vector接口。
- List接口:有序但不唯一,添加顺序,有两个实现类:
-
- ArryList:有序,不唯一;底层是数组。查询,修改快。
-
- LinkList:无序,唯一。底层是链表。添加删除快。
- Set接口:无序,唯一,两个实现类:HashSet和TreeSet。
- HashSet:无序唯一,底层数据结构为哈希表。重写hashCode和equals方法。
- TreeSet:有序唯一,底层为二叉树,但必须实现Comparable或Comparator接口。
3.2Map接口:
键值对key,value形式存储数据,键不能重复,值可以。
3.2.1 Map接口分别两个实现类
- HashSet:底层为哈希表,key无序,唯一。
- TreeSet: 底层为二叉树,key有序,唯一。
4 List
4.1 底层原理
4.1.1 ArryList
ArrayList是List接口的可变数组非同步实现,并允许包括null在内的所有元素。底层使用数组实现该集合是可变长度数组,数组扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量增长其容量的1.5倍,这种操作的代价很高。采用了Fail-Fast机制,面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险remove方法会让下标到数组末尾的元素向前移动一个单位,并把最后一位的值置空,方便GC。
优点:操作读取操作效率高,基于数组实现的,可以为null值,可以允许重复元素,有序,异步。
缺点:由于它是由动态数组实现的,不适合频繁的对元素的插入和删除操作,因为每次插入和删除都需 要移动数组中的元素。
4.1.2 LinkedList
LinkedList底层是由双向链表的数据结构实现的。
由上图可以看到:双向链表是由三个部分组成:prev、data、next.
prev:由用来存储上一个节点的地址;
data:是用来存储要存储的数据;
next:是用来存储下一个节点的地址。
上图可以看出双向链表每个元素之间的联系。我故意将每个链表画的分布不均匀是因为它不像数组一样是连续排列的,双向链表是可以占用一段不连续的内存空间。
当我们有新元素插入时,只需要修改所要插入位置的前一个元素的next值和后一个元素的prev值即可。比如我们在数据2与数据6之间插入一个数据4的元素,那么只需要修改数据2的next值和数据6的prev值。如下图
删除也是同理,比如要删除数据8的元素,只需要修改数据7的next值和数据9的prev值即可,然后数据8没有元素指向它,它就成了垃圾对象,最后被回收。因此在增加和删除的时候只需要更改前后元素的next和prev值,效率非常高。但是在查询的时候需要从第一个元素开始查找,直到找到我们需要的数据为止,因此查询的效率比较低。
4.2线程不安全
Array List是线程不安全的,这点很重要,也是面试最常问的问题。那为什么是不安全的呢,下面简单总结以下。
情况一:
假设现在有A,B两个线程同时执行add方法,而现在size = 9,于是:
1,A经过以上步骤发现初始化容量为10,不需要进行数组扩容。
2,同时B也在执行add方法,它判断数组初始化容量也是10(size的值还是9),于是不进行数组扩容。接着A便执行add方法,元素添加成功后,size = 10;此时B开始执行add方法,但这个时候数组元素已经添加满了,B再添加就会造成数组下标越界异常。
情况二:
elementData[size++] = e;
这一步也有可能出现问题。
列表大小为0,即size=0。
线程A开始添加一个元素,值为A。此时它执行第一条操作,将A放在了elementData下标为0的位置上。
接着线程B刚好也要开始添加一个值为B的元素,且走到了第一步操作。此时线程B获取到size的值依然为0,于是它将B也放在了elementData下标为0的位置上。
而最终size大小为:
- 线程A开始将size的值增加为1
- 线程B开始将size的值增加为2
针对这种情况,可以使用以下方式解决:
List<String> list1 = Collections.synchronizedList(new ArrayList<String>());
5 Map
5.1 关于缓存
准备金系统有完整的利用Map创建的缓存机制工具类。
参看链接https://www.cnblogs.com/henuyuxiang/p/7486120.html也有缓存的例子。
5.1 底层原理
5.1.1 底层管理
参看链接https://www.cnblogs.com/vole/p/12164982.html
5.1.2 关于hashcode() 与equals()方法
参看链接https://www.cnblogs.com/vole/p/12165284.html
6 遍历方式
6.1 迭代器
- 在开发中,经常使用的还是for-each循环来遍历来Collection,不经常使用Iterable(迭代器)的,下面记录一下terable是一般用法:
- 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
- Java中的Iterator功能比较简单,并且只能单向移动.
list l = new ArrayList(); l.add("aa"); l.add("bb"); l.add("cc"); for (Iterator iter = l.iterator(); iter.hasNext();) { String str = (String)iter.next(); System.out.println(str); } /*迭代器用于while循环 Iterator iter = l.iterator(); while(iter.hasNext()){ String str = (String) iter.next(); System.out.println(str); } */
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
6.2 加强for循环
7 常用方法
* 【1】增 add(Object obj);addAll(Collection col);add(int index,Object obj) * 【2】删 clear(),remove(Object obj),remove(int index) * 【3】改 set(int index,Object obj) * 【4】查size(),itrator(),listIterator(),get(int index) * 【5】判 isEmpty(),contains(Object obj)
四、链表与二叉树
1.1 链表
-
链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
- 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
-
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素。
-
增删元素快:
-
增加元素:只需要修改连接下个元素的地址即可。
-
删除元素:只需要修改连接下个元素的地址即可。
-
1.2,红黑树
红黑树是一种自平衡二叉查找树,是计算机科学领域中的一种数据结构,典型的用途是实现关联数组,存储有序的数据。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
红黑树的约束:
- 节点可以是红色的或者黑色的。
- 根节点是黑色的。
- 叶子节点(特指空节点)是黑色的。
- 每个红色节点的子节点都是黑色的。
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同。
红黑树的特点:
速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍。