Collection集合
Collection与Map继承关系
* 1.内存层面需要针对多个数据进行存储,此时可以考虑的容器有:数组 集合 * * 2.数组存储的特点: * 数组一旦初始化,其长度就是确定的 * 数据中的多个元素是依次紧密排列的、有序的、可重复的 * 数组一旦初始化完成,其元素的类型就是确定的 * 元素的类型既可以是基本数据类型,也可以是引用数据类型 * * 3.Java集合架构体系(java.util包下) * java.util.Collection:存储一个一个数据 * 子接口: * * List:有序的、可重复的数据(动态数组) * 实现类:ArrayList LinkedList Vector * * Set:无序的、不可重复的数据(集合) * 实现类:HashSet LinkedHashSet TreeSet * * java.util.Map:存储一对一对的数据(key-value键值对 y=f(x),类似于函数) * 实现类: * HashMap LinkedHashMap TreeMap Hashtable(子:Properties) *
* 1.Collection中15个抽象方法 * add(Object obj) * addAll(Collection coll)装的是集合 * clear() * isEmpty() * size() * contains(Object obj) * containsAll(Collection coll) * removeAll(Collection coll)删交集 * retainAll(Collection coll)保留交集 * equals() * toArray() * iterator() *2. * List<> Arrays.asList(Object) 数组->集合 * Object[] Arrays.toArray() 集合->数组 * *3.向Collection中添加元素的要求: * 要求元素所属的类一定要重写equals()方法 * 原因:Collection中的相关方法(contains()/remove())在使用时,要调用元素所属类的equals方法 *
@Test
public void test1() {
/*
* Collection接口
* */
//add(Object)
Collection collection = new ArrayList();
collection.add("AA");
collection.add(123);
collection.add("尚硅谷");
collection.add(new Person(13, "Tom"));
System.out.println(collection);
//addAll(Collection)
Collection collection1 = new ArrayList();
collection1.add("BB");
collection1.add("Jerry");
collection.addAll(collection1);
System.out.println(collection);
//size()元素个数
System.out.println(collection.size());
//如果
collection.add(collection1);
System.out.println(collection);
System.out.println(collection.size());
/*
* (4)boolean isEmpty():判断当前集合是否为空集合
* (5)boolean contains(Object obj):判断当前集合中是否存在一个与 obj 对象 equals 返回 true 的元素
* (6)boolean containsAll(Collection coll):判断 coll 集合中的元素是否在当前集合中都存在。即 coll 集合是否是当前集合的“子集”
* (7)boolean equals(Object obj):判断当前集合与 obj 是否相等
*
* */
//contains(Object obj)底层是equals实现,取决于equals是否被重写,Object中的equals就是==
System.out.println(collection.contains(123));
System.out.println(collection.contains(new String("AA")));//重写了equals比较内容
System.out.println(collection.contains(new Person(13, "Tom")));
}
@Test
public void test2() {
/*
* (8)void clear():清空集合元素
* (9) boolean remove(Object obj) :从当前集合中删除第一个找到的与 obj 对象 equals 返回 true 的元素。
* (10)boolean removeAll(Collection coll):从当前集合中删除所有与 coll 集合中相同的元素。即 this = this - this ∩ coll
* (11)boolean retainAll(Collection coll):从当前集合中删除两个集合中不同的元素,使得当前集合仅保留与 coll 集合中的元素相同的元素,即当前集合中仅保留两个集合的交集,即 this = this ∩ coll;
*
* */
Collection collection = new ArrayList();
collection.add("AA");
collection.add("BB");
collection.add(new Person(24, "Tom"));
//clear一个一个清掉
System.out.println(collection.size());
System.out.println(collection);
//collection.clear();
System.out.println(collection.size());
System.out.println(collection);
//remove
collection.remove("BB");
System.out.println(collection);
// removeAll删交集
//retainAll保留交集
}
@Test
public void test3() {
/*
* (12)Object[] toArray():返回包含当前集合中所有元素的数组
* (13)hashCode():获取集合对象的哈希值
* (14)iterator():返回迭代器对象,用于集合遍历
* */
Collection collection = new ArrayList();
collection.add("AA");
collection.add("BB");
collection.add(new Person(24, "Tom"));
//toArray() 集合->数组
Object[] array = collection.toArray();
System.out.println(Arrays.toString(array));
//asList()数组->集合
String[] arr = new String[]{"AA", "CC", "BB"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
}
@Test
public void test4() {
Integer[] integers = new Integer[]{1, 2, 3};
List<Integer> list1 = Arrays.asList(integers);
System.out.println(list1.size());
System.out.println(list1);
int[] ints=new int[]{1,2,3};
List<int[]> list2 = Arrays.asList(ints);//这里会先进行包装
System.out.println(list2.size());
System.out.println(list2);
}
Iterator-用于遍历集合元素
Collection 接口继承了 java.lang.Iterable 接口,该接口有一个 iterator()方法,那么所有实现了 Collection 接口的集合类都有一个 iterator()方法,用以返回一个实现了 Iterator接口的对象。
@Test
public void test1() {
Collection collection = new ArrayList();
collection.add("AA");
collection.add("BB");
collection.add(new Person(24, "Tom"));
Iterator iterator = collection.iterator();
//方式一
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
//方式二
// for (int i = 0; i < collection.size(); i++) {
// System.out.println(iterator.next());
// }
//方式三(推荐)
while (iterator.hasNext()) {//判断下一位是否还有元素
System.out.println(iterator.next());//①指针下移②将下移后所指向的元素返回
}
//方式四(增强for循环)(JDK5.0)
/*
* 用途:遍历集合、数组
*
* 格式:for(容器内元素的类型(要遍历的集合或数组元素类型) 临时变量:容器对象名(要遍历的集合或数组变量)){}
* 原理:collection中取出的元素临时赋给obj
* 底层使用的是迭代器
* */
for (Object obj : collection) {
System.out.println(obj);
}
}
Collection子接口List
* List:有序的、可重复的数据(动态数组)
* 实现类:ArrayList LinkedList Vector
* ArrayList:List主要实现类,线程不安全,底层是Object[] (添加 查询)
* LinkedList:底层使用双向链表的方式进行存储 (插入 删除)
* Vector:List古老实现类,线程安全,底层是Object[]
*
* 2.List中常用方法 * 1.Collection中的15个方法 * 2.因为List是有序的,因而增加一些针对索引的方法 * • 插入元素 – void add(int index, Object ele):在 index 位置插入 ele 元素 – boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来 • 获取元素 – Object get(int index):获取指定 index 位置的元素 – List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合 • 获取元素索引 – int indexOf(Object obj):返回 obj 在集合中首次出现的位置 – int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置 • 删除和替换元素 – Object remove(int index):移除指定 index 位置的元素,并返回此元素 – Object set(int index, Object ele):设置指定 index 位置的元素为ele * *小结: * 增: * add(Object obj) * addAll(Collection coll) * 删: * remove(Object obj) * remove(int index) * 改: * set(int index, Object ele) * 查: * get(int index) * 插: * add(int index, Object ele) * addAll(int index, Collection eles) * 长度: * size() * 遍历: * iterator() * foreach * for
@Test
public void test1() {
List list = new ArrayList();
list.add("AA");
list.add("BB");
list.add(new Person(23, "Tom"));
list.add(123);
System.out.println(list);
list.add(2, "CC");
System.out.println(list);
List list1 = Arrays.asList(1, 2, 3);
//list.add(1,list1);//将list1整体作为一个元素插入
list.addAll(1, list1);
System.out.println(list);
}
@Test
public void test2() {
List list = new ArrayList();
list.add("AA");
list.add("BB");
list.add(new Person(23, "Tom"));
list.add(123);
list.add(2);
System.out.println(list);
list.remove(2);
System.out.println(list);
//如何删除数据int 2
list.remove(Integer.valueOf(2));
System.out.println(list);
//遍历
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
for (Object obj : list) {
System.out.println(obj);
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
Collection子接口Set
* 1.Set:无序的、不可重复的数据(集合) * 常用方法:和Collection一样 Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。 Set 集合支持的遍历方式和 Collection 集合一样:foreach 和 Iterator。 * 实现类:HashSet LinkedHashSet TreeSet * HashSet:底层使用的是HashMap,即使用数组+单向链表+红黑树结构进行存储(jdk8中)。 * LinkedHashSet:是HashSet的子类类,在现有的数组+单向链表+红黑树结构基础上,又添加了了一组双向链表,用于记录添加元素的先后顺序,即我们可以通过添加元素的顺序实现遍历。便于频繁的查询操作。 * TreeSet:底层使用红黑树存储。可以按照添加元素的指定属性进行遍历 * * 2.开发中的场景 * 较List、Map来说,Set的使用频率较少 * 用来过滤重复数据 * * 3.Set中无序性和不可重复性的理解 * 无序性:!=随机性;不是指添加元素的顺序和遍历元素顺序不一致;什么是无序性:和添加的元素位置有关,不像ArrayList一样是依次紧密排列的。根据添加元素的哈希值,计算其在数组中的存储位置,此位置不是依次排列的,表现为无序性。 * 不可重复性:添加到Set中的元素是不能相同的。比较的标准 ,需要判断hashCode()得到的哈希值以及equals()得到的boolean值的结果。哈希值相同且equals返回结果是是true则认为元素是相同的。 * * 4.添加到HashSet和LinkedHashSet中元素的要求: * 要求元素所在类重写equals和hashCode两个方法。 * * 5.TreeSet的使用: * ①向TreeSet中添加元素的要求: * 要求添加到TreeSet中的元素必须是同一类型的对象,否则会报ClassCastException的异常。 * ②判断数据相同的标准 * 不再是考虑hashCode()和equals(),也就意味着添加到TreeSet中的元素不需要重写这两个方法 * 比较元素大小或比较元素是否相等的标准就是考虑自然排序或定制排序中,compareTo()或compare()的返回值。返回值为0则相等,由于TreeSet中不能添加相同元素,则第二个相等的以及之后不能添加进去。 * 添加的元素需要考虑排序:①自然排序②定制顺序 *
HashSet
• HashSet 是 Set 接口的主要实现类,大多数时候使用 Set 集合时都使用这个实现类。
• HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存储、查找、删除性能。
• HashSet 具有以下特点
:
– 不能保证元素的排列顺序
– HashSet
不是线程安全
的
–
集合元素可以是 null
• HashSet 集合判断两个元素相等的标准
:两个对象通过
hashCode()
方法得到的哈希值相等,并且两个对象的 equals()
方法返回值为 true。
• 对于存放在 Set 容器中的对象,对应的类一定要重写 hashCode()和 equals(Object
obj)方法
,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
• HashSet 集合中元素的无序性,不等同于随机性。这里的无序性与元素的添加位置有关。具体来说:我们在添加每一个元素到数组中时,具体的存储位置是由元素的hashCode()调用后返回的 hash 值决定的。导致在数组中每个元素不是依次紧密存放的,表现出一定的无序性。
HashSet 中添加元素的过程:
• 第 1 步:当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的hashCode() 方法得到该对象的 hashCode 值,然后根据 hashCode 值,通过某个散列函数决定该对象在 HashSet 底层数组中的存储位置。
• 第 2 步:如果要在数组中存储的位置上没有元素,则直接添加成功。
• 第 3 步:如果要在数组中存储的位置上有元素,则继续比较:
– 如果两个元素的 hashCode 值不相等,则添加成功;
– 如果两个元素的 hashCode()值相等,则会继续调用 equals()方法:
• 如果 equals()方法结果为 false,则添加成功。
• 如果 equals()方法结果为 true,则添加失败。第 2 步添加成功,元素会保存在底层数组中。第 3 步两种添加成功的操作,由于该底层数组的位置已经有元素了,则会通过链表的方式继续链接,存储。
LinkedHashSet
• LinkedHashSet 是 HashSet 的子类,不允许集合元素重复。
• LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向
链表
维护元素的次序,这使得元素看起来是以
添加顺序
保存的。
• LinkedHashSet 插入性能略低
于 HashSet,但在
迭代访问
Set 里的全部元素时有很好的性能。
TreeSet
Map
Map 中的 key
用
Set
来存放
,
不允许重复
,即同一个 Map 对象所对应的类,须重写 hashCode()和 equals()方法
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value,不同 key 对应的 value
可以重复
。value 所在的类要重写 equals()方法。
key 和 value 构成一个 entry。所有的 entry 彼此之间是无序的、
不可重复的
。
* 1.Map及其实现类 * HashMap:主要实现类,线程不安全,效率高;可以添加null的key和value值;底层使用数组+单向链表+红黑树(JDK8) * LinkedHashMap:是HashMap的子类,增加双向链表用于记录添加元素的先后顺序,进而在遍历时可以使用此顺序显示。便于频繁的遍历操作。 * * TreeMap:底层使用红黑树存储;可以按照添加key-value中的key元素的指定属性的大小顺序进行遍历。需要考虑使用①自然排序 ②定制排序 * * Hashtable:古老实现类,线程安全;不可以添加null的key或value值;底层使用数组+单向链表 * Properties:Hashtable的子类,其key和value都是String类型,常用于处理属性文件 * 2.HashMap中元素的特点 * HashMap中所有的key是不可重复的、无序的。所有的key构成一个set集合。key所在的类要重写hashCode()和equals(). * HashMap中的value是可重复的、无序的。所有的value就构成一个Collection集合。value所在的类重写equals。 * HashMap中的一个key-value就构成一个Entry。HashMap中所有的Entry是不可重复的、无序的。所有的Entry就构成一个Set集合。 * * 3.Map常见方法 * 增: * put(key,value) * putAll(Map) * 删: * Object(value) remove(key) * 改: * put(key,value) * putAll(Map) * 查: * get(key) * 长度: * size() * 遍历: * 遍历key集:Set keySet() * 遍历value集:Collection values() * 遍历entry集:Set entrySet() *
@Test
public void test1(){
HashMap hashMap = new HashMap();
hashMap.put("AA",56);
hashMap.put(67,56);
hashMap.put("Tom",56);
System.out.println(hashMap);
System.out.println(hashMap.remove(67));
System.out.println(hashMap.put("AA", 78));//返回OldValue 务必重写equals
System.out.println(hashMap.get("Tom"));
Set keySet = hashMap.keySet();
Iterator iterator = keySet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
// Collection values = hashMap.values();
// for(Object obj:values){
// System.out.println(obj);
// }
for (Object key:keySet){
Object value = hashMap.get(key);
System.out.println(value);
}
}
@Test
public void test2(){
HashMap hashMap = new HashMap();
hashMap.put("AA",56);
hashMap.put(67,56);
hashMap.put("Tom",56);
Set entrySet = hashMap.entrySet();
Iterator iterator =entrySet.iterator();
while (iterator.hasNext()){
Map.Entry entry=(Map.Entry) iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
Hashtable 和 HashMap 的区别
HashMap:底层是一个哈希表(
jdk7:
数组
+
链表
;jdk8:
数组
+
链表
+
红黑树)
,
是一个线程不安全的集合,
执行效率高
Hashtable:底层也是一个哈希表(数组
+
链表)
,
是一个线程安全的集合
,
执行效率低
HashMap 集合
:
可以存储
null
的键、
null
的值
Hashtable 集合
,
不能存储
null
的键、
null
的值
Hashtable 和
Vector
集合一样
,
在
jdk1.2
版本之后被更先进的集合
(HashMap,ArrayList)取代了。所以
HashMap
是
Map
的主要实现类,
Hashtable
是
Map
的古老实现类。
Hashtable 的子类
Properties
(配置文件)依然活跃在历史舞台
Properties 集合是一个唯一和
IO
流相结合的集合
Collections工具类
* 1.Collections概述: * Collections是一个操作Set,List,Map等集合的工具类 * 2.常用方法 * 3.区分Collection和Collections * Collection:集合框架中用来存储一个个元素的接口,又分为List和Set等子接口。 * Collections:用于操作集合框架的一个工具类 *
排序操作:
• reverse(List):反转 List 中元素的顺序
• shuffle(List):对 List 集合元素进行随机排序
• sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
• sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
• swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找
• Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
• Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
• Object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素
• Object min(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最小元素
• int binarySearch(List list,T key)在 List 集合中查找某个元素的下标,但是 List 的元素必须是 T 或 T 的子类对象,而且必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定。
• int binarySearch(List list,T key,Comparator c)在 List 集合中查找某个元素的下标,但是List 的元素必须是 T 或 T 的子类对象,而且集合也事先必须是按照 c 比较器规则进行排序过的,否则结果不确定。
• int frequency(Collection c,Object o):返回指定集合中指定元素的出现次数
复制、替换
• void copy(List dest,List src):将 src 中的内容复制到 dest 中
• boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
• 提供了多个 unmodifiableXxx()方法,该方法返回指定 Xxx 的不可修改的视图。
添加
• boolean addAll(Collection c,T... elements)将所有指定元素添加到指定 collection 中。
同步
• Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
@Test
public void test1(){
List list = Arrays.asList(45, 65, 3, 45, 123, 65, 67, 83, 20);
System.out.println(list);
//反转
Collections.reverse(list);
System.out.println(list);
//对元素进行随机排序
Collections.shuffle(list);
//升序排序
Collections.sort(list);
//自定义排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Integer&&o2 instanceof Integer){
Integer i1=(Integer) o1;
Integer i2=(Integer) o2;
return -(i1-i2);
}//降序
throw new RuntimeException("类型不匹配");
}
});
System.out.println(list);
//交换位置
Collections.swap(list,1,3);
System.out.println(list);
}
@Test
public void test2(){
List list = Arrays.asList(45, 65, 3, 45, 123, 65, 67, 83, 20);
System.out.println(list);
Object max = Collections.max(list);//取最右边,自然排序
System.out.println(max);//123
Object max1=Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Integer&&o2 instanceof Integer){
Integer i1=(Integer) o1;
Integer i2=(Integer) o2;
return -(i1-i2);
}//降序
throw new RuntimeException("类型不匹配");
}
});
System.out.println(max1);//3
//出现的频率
int frequency = Collections.frequency(list, 45);
System.out.println(frequency);
//复制
List dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest,list);
System.out.println(dest);
}
@Test
public void test3(){
//unmodifiableXxx返回一个只能读不能写的视图
ArrayList arrayList = new ArrayList();
arrayList.add(12);
arrayList.add(23);
arrayList.add(52);
List unmodifiableList = Collections.unmodifiableList(arrayList);
System.out.println(unmodifiableList);
}
@Test
public void test4(){
ArrayList arrayList = new ArrayList();
List list = Collections.synchronizedList(arrayList);
//返回线程安全的list
}
数据结构
* 数据结构,是一种程序优化的方法论,研究数据的“逻辑结构”和“物理结构”以及它们之间的关系,并针对这种结构定义相应的运算,目的是加快程序的执行速度、减少内存占用空间。 *1.数据结构的研究对象 * 研究对象1:数据之间的逻辑关系 * 集合关系 * 线性关系:一对一关系 * 树形结构:一对多关系 * 图形结构:多对多关系 * 研究对象2:数据的存储结构(物理结构) * 顺序结构 * 链式结构 * 索引结构 * 散列结构 * 开发中,习惯上的存储结构: * 线性表(一对一关系):一维数组、单向链表、双向链表、栈、队列 * 树(一对多关系):二叉树、B+树(MySQL) * 图(多对多) * 哈希表:HashMap、HashSet * * 研究对象3:运算结果(相关算法操作) * 分配资源,建立结构,释放资源 * 插入和删除 * 获取和遍历 * 修改和排序 * *2.常见存储结构之:数组 *
*3.常见存储结构之:链表
* 链表中的基本结构是节点(Node)
* 3.1单向链表
@Test
public void test1() {
Node node1 = new Node("AA");
Node node2 = new Node("BB");
node1.next = node2;
}
class Node {
//单向链表
Object data;
Node next;
public Node(Object data) {
this.data = data;
}
}
* 3.2双向链表
@Test
public void test2() {
DoubleNode node1 = new DoubleNode(null, "AA", null);
DoubleNode node2 = new DoubleNode(node1, "BB", null);
DoubleNode node3 = new DoubleNode(node2, "AA", null);
node1.next = node2;
node2.next = node3;
}
class DoubleNode {
//双向链表
DoubleNode previous;
Object data;
DoubleNode next;
public DoubleNode(Object data) {
this.data = data;
}
public DoubleNode(DoubleNode previous, Object data, DoubleNode next) {
this.previous = previous;
this.data = data;
this.next = next;
}
}
*4.常见存储结构之:栈
* 属于抽象数据类型(ADT):可以使用数组或链表来模拟构建
@Test
public void test4() {
Stack stringStack = new Stack(10);
//stringStack.pop();
stringStack.push("AA");
System.out.println(stringStack.pop());
}
class Stack {
//先进后出
//用数组模拟栈结构
private Object[] values;
private int size;//记录存储的元素个数
public Stack(int length) {//初始化数组
values = new Object[length];
}
//入栈
public void push(Object element) {
if (size >= values.length) throw new RuntimeException("栈空间已满,入栈失败");
values[size] = element;
size++;
}
//出栈
public Object pop() {
if (size <= 0) throw new RuntimeException("栈空间已空,出栈失败");
Object value = values[size - 1];
values[size - 1] = null;
size--;
return value;
}
}
*5.常见存储结构之:队列
class Queue {
//先进先出
//用数组模拟队列
Object[] values;
int size;//用于记录元素的个数
public Queue(int length) {
values = new Object[length];
}
public void add(Object element) {
if (size >= values.length) throw new RuntimeException("队列已满,添加失败");
values[size] = element;
size++;
}
public Object get() {
if (size <= 0) throw new RuntimeException("队列已空,获取失败");
Object value = values[0];
for (int i = 0; i < size - 1; i++) {
values[i] = values[i + 1];
}
values[size - 1] = null;
size--;
return value;
}
}
*6.常见存储结构之:二叉树
* 属于链表衍生出的结构 * 先序遍历:根左右 第一个根节点 * 中序遍历:左根右 (二叉排序树,从小到大遍历)与先序可判断左右子树 * 后序遍历:左右根
@Test
public void test3() {
TreeNode node1 = new TreeNode(null, "AA", null);
TreeNode leftNode1 = new TreeNode(null, "BB", null);
TreeNode rightNode1 = new TreeNode(null, "CC", null);
node1.left = leftNode1;
node1.right = rightNode1;
}
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;
}
}
* * 平衡二叉树:任意节点左右子树高度差小于等于1 * 平衡二叉树:左旋:右子树不平衡 * 确定支点:从添加节点开始,不断往父类节点找第一个不平衡的节点 * 把不平衡点作为支点,把支点左旋降级,变成左子节点 * 不平衡点的右子节点晋升 * 右旋:左子树不平衡 * 左左:(根节点的左子树的左子树有节点插入,导致二叉树不平衡) 一次右旋 * 左右:左子树局部左旋,在整体一次右旋 * 右右:一次左旋 * 右左:右子树局部右旋,在整体一次左旋 * * 红黑树:自平衡的二叉查找树 * ①每个节点是红色的或黑色的 * ②根节点是黑色的 * ③如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的。 * ④如果某一个节点是红色的,那么他的子节点必须是黑色的(只有两个红的不行) * ⑤对于每一个节点,从该节点到所有的后代叶节点的简单路径上,均包含相同数目的黑色节点。 * * 添加节点时如何调整:(默认颜色:添加的节点默认是红色的(效率高)) * 根节点是红色:直接变为黑色 * 添加非根节点:父节点黑色:不需要调整 * 父节点红色: * 叔叔节点红色:将父节点和叔叔节点设为黑色,若祖父为根节点不需要操作,若祖父不是根节点,将祖父节点设为红色,在当作当前节点进行其他判断。 * 叔叔节点黑色:(当前节点是父节点的右孩子)将父节点设置为当前节点并左旋,再进行判断 * (当前节点是父节点的左孩子)将父节点变黑,祖父变红,以祖父为支点进行右旋 * *
List
*一、ArrayList * 1.ArrayList的特点: * 实现了List接口,存储有序的、可重复的数据 * 底层使用Object[]数组存储 * 线程不安全 * * 2.ArrayList的源码解析: * 2.1 jdk7版本 * ArrayList<String> list=new ArrayList<>();==>Object[] elementData=new Object[10]; * list.add("AA");==>elementData[0]="AA"; * list.add("BB");==>elementData[1]="BB"; * ... * 当要添加第11个元素的时候,底层的elementData数组已满,需要扩容。默认扩容为原来长度的1.5倍,并将原数组元素复制过来。 * * 2.2 jdk8版本 * ArrayList<String> list=new ArrayList<>();==>Object[] elementData=new Object[]{}; * list.add("AA");==>首次添加元素时,会初始化数组elementData=new Object[10];elementData[0]="AA"; * list.add("BB");==>elementData[1]="BB"; * ... * 当要添加第11个元素的时候,底层的elementData数组已满,需要扩容。默认扩容为原来长度的1.5倍,并将原数组元素复制过来。 * *二、Vector * 1.Vector的特点 * 实现了List接口,存储有序的、可重复的数据 * 底层使用Object[]数组存储 * 线程安全 * * 2.Vector源码解析(jdk8): * Vector v=new Vector();//==>Object[] elementData=new Object[10];底层初始化数组,长度为10 * v.add("AA");==>elementData[0]="AA"; * v.add("BB");==>elementData[1]="BB"; * ... * 当要添加第11个元素的时候,底层的elementData数组已满,需要扩容。默认扩容为原来长度的2倍,并将原数组元素复制过来。 * *三、LinkList * 1.LinkList的特点 * 实现了List接口,存储有序的、可重复的数据 * 底层使用双向链表存储 * 线程不安全 * * 2.LinkList源码解析(jdk8) * LinkedList<String> list=new LinkedList<>(); * list.add("AA");//将"AA"封装到Node对象1中,list对象属性first、last都指向此Node对象1 * * list.add("BB");//将"BB"封装到Node对象2中,对象1和对象2构成一个双向链表,同时last指向此Node对象2 * * 3.LinkList是否存在扩容问题 * 双向链表,不需要考虑扩容问题 * *四、启示与开发建议 * 1.Vector基本不适用 * 2.ArrayList底层使用数组结构,查找和添加元素(尾部添加)效率高,时间复杂度为O(1);删除和插入效率低,时间复杂度O(n) * LinkedList底层使用双向链表结构,删除和插入效率高,时间复杂度为O(1);查找和添加(尾部添加)效率低,时间复杂度为O(n)(添加有可能是O(n)) *
Set
Set 的内部实现其实是一个 Map,Set 中的元素,存储在 HashMap 的 key 中。即 HashSet 的内部实现是一个 HashMap,TreeSet 的内部实现是一个TreeMap,LinkedHashSet 的内部实现是一个 LinkedHashMap。
Map
*一、HashMap * * 1.HashMap中元素的特点 * HashMap中所有的key是不可重复的、无序的。所有的key构成一个set集合。value所在的类要重写hashCode()和equals(). * HashMap中的value是可重复的、无序的。所有的value就构成一个Collection集合。value所在的类重写equals。 * HashMap中的一个key-value就构成一个Entry。HashMap中所有的Entry是不可重复的、无序的。所有的Entry就构成一个Set集合。 * 2.HashMap源码解析 * 2.1 jdk7中创建对象和添加数据的过程 * HashMap<String,Integer> map=new HashMap<>();==>Entry[] table=new Entry[16];//创建对象的过程中,底层会初始化数组 * map.put("AA",78);//将"AA"和78封装到一个Entry对象中,考虑将此对象添加到table数组中 * 添加/修改的过程: * 将(key1,value1)添加到当前map中: * 首先,需要调用key1所在类的hashCode()方法,计算key1对应的哈希值1,此哈希值1经过算法(hash())之后,得到哈希值2. * 哈希值2在经过算法(indexFor())之后,就确定了(key1,value1)在数组table中的位置i。 * 1.1 如果此索引位置i上没有元素,则(key1,value1)添加成功。(情况一) * 1.2 如果此索引位置i上有元素(key,value),则需要比较key1和key的哈希值2。 (哈希冲突) * 2.1 如果key1的哈希值2与key的哈希值2不相同,则(key1,value1)添加成功。(情况二) * 2.2 如果key1的哈希值2与key的哈希值2相同,则需要继续比较key1和key的equals()。要调用key1所在类的equals(),将key作为参数传递进去。 * 3.1 调用equals(),返回false:则(key1,value1)添加成功。(情况三) * 3.2 调用equals(),返回true:则认为key1和key是相同的。默认情况下,value1替代原来的value。 * 说明:情况一:将(key1,value1)存放到数组索引i的位置。 * 情况二、三:(key1,value1)与现有的(key,value)构成单向链表结构,(key1,value1)(头节点)指向(key,value)。 * 随着不断地添加元素,在满足如下条件下,会考虑扩容: * (size>=threshold)&&(null!=table[i]):出现链表 * 当元素个数达到临界值(数组长度*加载因子)时,就考虑扩容。默认的临界值=16*0.75=12 。默认扩容为原来的2倍。 * * 2.2 jdk8与jdk7的不同之处 * 1.当创建了HashMap实例以后,底层并没有初始化table数组。当首次添加(key,value)时,进行判断,如果发现table未初始化,则对数组进行初始化。 * 2.HashMap底层定义Node内部类,代替原来Entry内部类。意味着,我们创建的数组是Node[] * 3.如果当前的(key,value)经过一系列判断之后,可以添加到当前数组角标i中。如果此时角标i的位置上有元素。jdk7中新的(key,value)指向旧的(头插法),jdk8是尾插法。 * 4.jdk7:数组+单向链表; jdk8:数组+单向链表+红黑树 * 什么时候单向链表变为红黑树:如果数组索引i位置上的元素个数达到8,并且数组长度达到64,我们就将此索引i位置上的多个元素使用红黑树结构进行存储。 * (红黑树进行put()/get()/remove()操作时间复杂度为O(log n),单向链表O(n))。 * 什么时候红黑树变为单向链表:当使用红黑树索引i位置上元素个数低于6时,退化为单向链表。(考虑到空间复杂度) * 5.扩容:当单链表长度达到8且Node数组比较短(64)的情况下也考虑先扩容 * 2.3 属性/字段 * *二、LinkedHashMap * 1.LinkedHashMap与HashMap的关系 * LinkedHashMap是HashMap的子类:在HashMap基础上(数组+单向链表+红黑树),增加一对双向链表,记录添加的(key,value)的先后顺序。便于遍历所有的key-value。 * * 2.底层结构:LinkedHashMap内部定义了一个Entry * static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } } * *三、HashSet和LinkedHashSet * HashSet底层是HashMap * LinkedHashSet底层是LinkedHashMap *
Stream
* 1.StreamAPI vs 集合框架 * >StreamAPI关注的是数据的计算(排序、查找、过滤、映射、遍历等),面向CPU * 集合关注数据的存储,面向内存 * 2.说明 * ①Stream 自己不会存储元素。 * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream。 * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。 * ④Stream 一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。 * 3.Stream执行流程 * 步骤1:Stream的实例化 * 步骤2:中间操作 * >筛选和切片: * filter(Predicate p)——接收Lambda,从流中排除某些元素 * limit(n)——截断流,使元素不超过指定数量 * skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit相反 * distinct()——筛选,通过流所生成元素的hashCode()和equals()去除重复元素 * >映射: * map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素 * >排序: * 自然排序/定制排序 * * 步骤3:终止操作
@Test//Stream的实例化1
public void test1_1() {
//创建Stream的方式:通过集合
List<String> list = new ArrayList<>();
//default Stream<E> stream():返回一个顺序流
Stream<String> stream = list.stream();
//default Stream<E> parallelStream():返回一个并行流(效率高)
Stream<String> stringStream = list.parallelStream();
}
@Test//Stream的实例化2
public void test1_2() {
//创建Stream的方式:通过数组
//调用Arrays类的static <T> Stream<T> stream(T[] array):返回一个流
Integer[] arr = new Integer[5];
Stream<Integer> stream = Arrays.stream(arr);//并不存储,数据在数组中,操作数组内容
int[] arr1 = new int[5];
IntStream stream1 = Arrays.stream(arr1);
}
@Test//Stream的实例化3
public void test1_3() {
//创建Stream的方式:通过Stream的of方法
Stream<String> stream = Stream.of("AA", "CC");
}
@Test//中间操作:筛选与切片
public void test2_1() {
List<Person> list = DataBase.getPerson();
Stream<Person> stream = list.stream();
//查询年龄大于10岁的
//filter(Predicate p)——接收Lambda,从流中排除某些元素
/* stream.filter(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge>10;
}
});*/
stream.filter(person -> person.getAge() > 10).forEach(System.out::println);
//说明:forEach是终止操作,有终止操作才会执行中间操作
System.out.println();
//limit(n)——截断流,使元素不超过指定数量
list.stream().limit(5).forEach(System.out::println);
//说明:Stream 一旦执行了终止操作,就不能再调用其它中间操作或终止操作了,所以list.stream()生成新的stream
System.out.println();
//查询年龄大于10岁的的前2个
list.stream().filter(person -> person.getAge() > 10).limit(2).forEach(System.out::println);
System.out.println();
//skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit相反
list.stream().skip(7).forEach(System.out::println);//跳过前2个
System.out.println();
//distinct()——筛选,通过流所生成元素的hashCode()和equals()去除重复元素
list.stream().distinct().forEach(System.out::println);
//说明:需要重写hashCode()和equals()
}
@Test//中间操作:映射
public void test2_2() {
//map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素
//例:转换大小写
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
Stream<String> stream = list.stream();
stream.map(str -> str.toUpperCase()).forEach(System.out::println);
//例:获取姓名长度大于3的员工
System.out.println();
List<Person> person = DataBase.getPerson();
Stream<Person> stream1 = person.stream();
stream1.filter(per -> per.getName().length() > 3).forEach(System.out::println);
//例:获取姓名长度大于3的员工姓名
System.out.println();
person.stream().filter(per -> per.getName().length() > 3).map(per -> per.getName()).forEach(System.out::println);
}
@Test//中间操作:排序
public void test2_3() {
//sorted()——自然排序
Integer[] arr = new Integer[]{12, 324, 5, 346, 654, 4, 65};
String[] arr1 = new String[]{"GG", "DD", "SS", "MM", "JJ"};
Arrays.stream(arr).sorted().forEach(System.out::println);//升序
Arrays.stream(arr1).sorted().forEach(System.out::println);//升序
//sorted(Comparator com)——定制排序
List<Person> list = DataBase.getPerson();
list.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).forEach(System.out::println);
}
@Test//终止操作:匹配与查找
public void test3_1() {
List<Person> list = DataBase.getPerson();
//allMatch(Predicate p)——检查是否匹配所有元素
//练习:是否所有员工年龄大于12
System.out.println(list.stream().allMatch(per -> per.getAge() > 12));
//anyMatch(Predicate p)——检查至少有匹配一个元素
//练习:是否存在年龄大于12岁的人
System.out.println(list.stream().anyMatch(per -> per.getAge() > 12));
//findFirst()——返回第一个元素
System.out.println(list.stream().findFirst());
}
@Test//终止操作:获取个数
public void test3_2() {
List<Person> list = DataBase.getPerson();
//count——返回流中元素的个数
System.out.println(list.stream().count());
//max(Comparator c)——返回流中最大值
//例:返回最高的年龄
System.out.println(list.stream().max((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge())));
System.out.println(list.stream().max((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge())).get().getAge());
System.out.println(list.stream().map(person -> person.getAge()).max((s1, s2) -> Integer.compare(s1, s2)));
System.out.println(list.stream().map(Person::getAge).max(Integer::compare));
//min(Comparator c)——返回流中最小值
//forEach(Consumer c)——内部迭代
}
@Test//规约
public void test3_3() {
//reduce(T identify,BinaryOperator)——可将流中元素反复结合起来,得到一个值,返回T
//练习:计算1-10自然数之和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2));
System.out.println(list.stream().reduce(0, Integer::sum));
//reduce(BinaryOperator)——将流中元素反复结合起来,得到一个值,返回Optional<T>
//练习:计算所有人年龄
List<Person> person = DataBase.getPerson();
System.out.println(person.stream().map(per -> per.getAge()).reduce(Integer::sum));
System.out.println(person.stream().map(Person::getAge).reduce(Integer::sum));
}
@Test//收集
public void test3_4() {
//collect(Collector c)——将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
List<Person> list = DataBase.getPerson();
//练习1:查找年龄大于12的人,结果返回一个List/Set
List<Person> list1 = list.stream().filter(per -> per.getAge() > 12).collect(Collectors.toList());
list1.forEach(System.out::println);
//练习2:按照年龄排序,返回到一个新的List中
List<Person> list2 = list.stream().sorted((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge())).collect(Collectors.toList());
list2.forEach(System.out::println);
}
@Test//针对于集合,jdk8中增加了一个遍历的方法
public void test4() {
List<Person> list = DataBase.getPerson();
list.forEach(System.out::println);
/*
针对于List来说,常见的遍历方式:
①Iterator
②增强for
③一般for
④forEach()
* */
}
}
class DataBase {
public static List<Person> getPerson() {
List<Person> list = new ArrayList<>();
list.add(new Person("Tom", 12));
list.add(new Person("Jenny", 10));
list.add(new Person("Banny", 8));
list.add(new Person("KAN", 13));
list.add(new Person("Tank", 16));
list.add(new Person("TaoB", 1));
list.add(new Person("Dog", 2));
list.add(new Person("Tom", 12));
return list;
}
}
Regex
*正则表达式:用于校验字符串是否满足一定的规则,并用来校验数据格式的合法性 * 在一段文本中查找满足要求的内容 * 字符类:只匹配一个字符 * [abc] 只匹配abc []表示其中内容出现一次 * [^abc] 除了abc * [a-zA-Z] a-z或A-Z 等同于[a-z[A-Z]] * [a-z&&[def]] a-z与def的交集,即def * [a-z&&[^bc]] a-z与非bc的交集,即[ad-z]也写作[a[d-z]] * [a-z&&[^m-p]] a-z和 除了m-p的交集 * 预定义字符:只匹配一个字符 * . 任何字符 * \d 一个数字[0-9] * \D 非数字[^0-9] * \s 一个空白字符[\t\n\x0B\f\r] * \S 非空白字符[^\s] * \w [a-zA-Z_0-9]英文、数字、下划线 * \W [^\w]一个非单词字符 * 数量词: * X? X,一次或0次 * X* X,零次或多次 * X+ X,一次或多次 * X{n} X,正好n次 * X{n,} X,至少n次 * X{n,m} X,[n-m]次
@Test
public void testRegexDemo1() {
//[abc] 只能是abc中的一个
System.out.println("a".matches("[abc]"));//true
System.out.println("ab".matches("[abc]"));//false
System.out.println("ab".matches("[abc][abc]"));//true 第一个在第一个校验,第二个在第二个校验
System.out.println("a".matches("[abc][abc]]"));//false
}
@Test
public void testRegexDemo2() {
//[^abc]除了abc ^表示除了
System.out.println("a".matches("[^abc]"));//false
System.out.println("z".matches("[^abc]"));//true
System.out.println("zz".matches("[^abc]"));//false
}
@Test
public void testRegexDemo3() {
//[a-zA-Z] a-z A-Z
System.out.println("a".matches("[a-zA-Z]"));//true
System.out.println("A".matches("[a-zA-Z]"));//true
System.out.println("az".matches("[a-zA-Z]"));//false
System.out.println("aZ".matches("[a-zA-Z]"));//false
}
@Test
public void testRegexDemo4() {
//[a-d[m-p]] a-d,或m-p (和Demo3一样,为了提高代码阅读性)
System.out.println("a".matches("[a-d[m-p]]"));//true
System.out.println("e".matches("[a-d[m-p]]"));//false
System.out.println("am".matches("[a-d[m-p]]"));//false
}
@Test
public void testRegexDemo5() {
//[a-z&&[def]] a-z和def的交集,即def中的一个 如果写成了&则表示简单的&符号
System.out.println("a".matches("[a-z&[def]]"));//true
System.out.println("&".matches("[a-z&[def]]"));//true
System.out.println("a".matches("[a-z&&[def]]"));//false
System.out.println("d".matches("[a-z&&[def]]"));//true
System.out.println("de".matches("[a-z&&[def]]"));//false
}
//[a-z&&[^bc]] z-z和非bc的交集 等同于[ad-z]
//[a-z&&[^m-p]]
@Test
public void testRegexDemo6() {
System.out.println("\"");//打印" 转义字符\,改变了含义
//.表示任意一个字符
System.out.println("你".matches("."));//true
System.out.println("你".matches(".."));//false
System.out.println("ni".matches(".."));//true
}
@Test
public void testRegexDemo7() {
// \\d表示只能是任意一位数字
System.out.println("a".matches("\\d"));//false
System.out.println("1".matches("\\d"));//true
System.out.println("22".matches("\\d"));//false
}
@Test
public void testRegexDemo8() {
System.out.println("dfahkg12_".matches("\\w{6,}"));//true
System.out.println("d2_".matches("\\w{6,}"));//false
}
@Test
public void testRegexDemo9() {
System.out.println("15s_".matches("[0-9a-z_]{4}"));//true
System.out.println("23dd".matches("[0-9a-zA-Z]{4}"));//true
System.out.println("25dF".matches("[\\w&&[^_]]{4}"));//true
}