一 java中的容器
1、容器(Container)定义
1.1 web容器
1.2 容器类
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(" "); 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]+" "); } } }
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]+" "); } }
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]+" "); } System.out.println(" =================================="); }
第二种声明方式
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
6 遍历方式
6.1 List的三种方式
迭代器iterator;for循环;foreach加强for循环。
创建实体类
public class News{ private int id; private String title; private String author; public News(int id, String title, String author) { super(); this.id = id; this.title = title; this.author = author; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
6.1.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中插入和删除元素。
import java.util.ArrayList;public class Demo01 { public static void main(String[] args) { ArrayList<News> list = new ArrayList<News>(); list.add(new News(1,"list1","a")); list.add(new News(2,"list2","b")); list.add(new News(3,"list3","c")); list.add(new News(4,"list4","d")); for (int i = 0; i < list.size(); i++) { News s = (News)list.get(i); System.out.println(s.getId()+" "+s.getTitle()+" "+s.getAuthor()); } }}
import java.util.ArrayList; public class Demo02 { public static void main(String[] args) { ArrayList<News> list = new ArrayList<News>(); list.add(new News(1,"list1","a")); list.add(new News(2,"list2","b")); list.add(new News(3,"list3","c")); list.add(new News(4,"list4","d")); for (News s : list) { System.out.println(s.getId()+" "+s.getTitle()+" "+s.getAuthor()); } } }
6.2 Map的遍历方式
6.2.1基本使用
Map<String, Integer> map = new HashMap<>(); map.put("a", 1); map.put("b", 2); boolean b = map.containsKey("3");//map中是否包含key为3的键值。 System.out.println("MyClass.testMap b=" + b); 除此之外还有常见的: map.remove("b"); map.clear(); if (map.isEmpty()) { System.out.println("MyClass.testMap isEmpty"); }
6.2.2五种遍历方法
1、
方法一: 这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。 Map<Integer, Integer> map = new HashMap<Integer, Integer>(); for (Map.Entry<Integer, Integer> entry : map.entrySet()) { System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); }
2、
方法二: 在for-each循环中遍历keys或values。(推荐) Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //遍历map中的键 for (Integer key : map.keySet()) { System.out.println("Key = " + key); } //遍历map中的值 for (Integer value : map.values()) { System.out.println("Value = " + value); }
该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。
3、
方法三: 使用Iterator遍历 使用泛型: Map<Integer, Integer> map = new HashMap<Integer, Integer>(); Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, Integer> entry = entries.next(); System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); 不使用泛型: Map map = new HashMap(); Iterator entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Map.Entry) entries.next(); Integer key = (Integer)entry.getKey(); Integer value = (Integer)entry.getValue(); System.out.println("Key = " + key + ", Value = " + value); } 你也可以在keySet和values上应用同样的方法。像这样: 使用泛型: Iterator<String> iterator = map.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); System.out.println("MyClass.testMap key=" + key); } Iterator<Integer> iterator2 = map.values().iterator(); while (iterator2.hasNext()){ Integer values = iterator2.next(); System.out.println("MyClass.testMap values="+values); }
不使用泛型:就不在说了,相信聪明的你也一定会的。
该种方式看起来冗余却有其优点所在。首先,在老版本java中这是惟一遍历map的方式。另一个好处是,你可以在遍历时调用iterator.remove()来删除entries,另两个方法则不能。根据javadoc的说明,如果在for-each遍历中尝试使用此方法,结果是不可预测的。
从性能方面看,该方法类同于for-each遍历(即方法二)的性能。
4、
方法四 :通过键找值遍历(效率低) Map<Integer, Integer> map = new HashMap<Integer, Integer>(); for (Integer key : map.keySet()) { Integer value = map.get(key); System.out.println("Key = " + key + ", Value = " + value); }
作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。如果你安装了FindBugs,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。
总结:
如果仅需要键(keys)或值(values)使用方法二。如果你使用的语言版本低于java 5,或是打算在遍历时删除entries,必须使用方法三。否则使用方法一(键值都要)。
5、
查找最大元素 public class TestArray { public static void main(String[] args) { double[] myList = {1.9, 2.9, 3.4, 3.5}; // 查找最大元素 double max = myList[0]; for (int i = 1; i < myList.length; i++) { if (myList[i] > max) max = myList[i]; } System.out.println("Max is " + max); } }
参看链接:https://blog.csdn.net/da_caoyuan/article/details/79819221
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,红黑树
红黑树是一种自平衡二叉查找树,是计算机科学领域中的一种数据结构,典型的用途是实现关联数组,存储有序的数据。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
红黑树的约束:
- 节点可以是红色的或者黑色的。
- 根节点是黑色的。
- 叶子节点(特指空节点)是黑色的。
- 每个红色节点的子节点都是黑色的。
- 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同。
红黑树的特点:
速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍。