Java基础学习
- 一、集合进阶
- 1.1 数据结构(树)
- 1.1.1 二叉查找树
- 1.1.2 二叉树的遍历
- 1.2 平衡二叉树
- 树的演变
- 1.3 数据结构 ( 平衡二叉树 )左旋
- 1.4 数据结构 ( 平衡二叉树 )右旋
- 数据结构 (平衡二叉树)需要转的四种情况
- 1.5 红黑树
- 添加节点规则
- 二、Set系列集合
- 2.1 HashSet
- 2.2 LinkedHashSet
- 2.3 TreeSet
- 2.4 综上几个集合的使用情况
- 2.5 双列集合
- 三、Map
- 3.1 Map的常见的API
- 3.2 Map遍历
- 3.3 HashMap
- 3.4 LinkedHashMap
- 3.5 TreeMap
- 3.6 可变参数
- 3.7 collections
- 四、不可变集合
- 4.1 创建不可变集合
一、集合进阶
1.1 数据结构(树)
节点:
度:
每一个节点的子节点数量二叉树:
二叉树中,任意节点的度<=2树高:
树的总层数根节点
: 最顶层的节点左子节点
: 左下方的节点右子节点
:右下方的节点根节点的左子树
: 蓝色虚线根节点的右子树
: 绿色虚线
1.1.1 二叉查找树
特点:
- 每一个节点上最多有两个子节点
- 任意节点左子树上的值都小于当前节点
- 任意节点右子树上的值都大于当前节点
规则:
小的存左边大的存右边一样的不存
1.1.2 二叉树的遍历
- 前序遍历: 从根结点开始,然后按照当前结点,左子结点,右子结点的顺序遍历
- 中序遍历:从最左边的子节点开始,然后按照左子结点,当前结点,右子结点的顺序遍历
- 后序遍历:从最左边的子节点开始,然后按照左子结点,右子结点,当前结点的顺序遍历
- 层序遍历: 从根节点开始一层一层的遍历
1.2 平衡二叉树
规则
:任意节点左右子树高度差不超过1
树的演变
起初是乱序的排列着二叉树,使得在查找数据的时候只能通过遍历所有的数据去查找,这样运行的速度太慢。
后面就出现了二叉查找树,使得在节点的左边都是比节点小的,右边都是比节点大的,这样使得查找的速度显著提升。
由于在这样排列的话,会出现一边的子节点数过多,是的查找的效率有降低,最后出现了平衡二叉树,解决了这个问题
1.3 数据结构 ( 平衡二叉树 )左旋
确定支点:
从添加的节点开始,不断的往父节点找不平衡的节点
步骤:
简单
- 以不平衡的点作为支点
- 把支点左旋降级,变成左子节点
- 晋升原来的右子节点
复杂
- 以不平衡的点作为支点
- 将根节点的右侧往左拉
- 原先的右子节点变成新的父节点,并把多余的左子节点出让,已经降级的根节点当右子节点
1.4 数据结构 ( 平衡二叉树 )右旋
步骤:
简单
- 以不平衡的点作为支点
- 把支点右旋降级,变成右子节点
- 晋升原来的左子节点
复杂
- 以不平衡的点作为支点
- 就是将根节点的左侧往右拉
- 原先的左子节点变成新的父节点,并把多余的右子节点出让,已经降级的根节点当左子节点
数据结构 (平衡二叉树)需要转的四种情况
左左: 一次右旋
左右: 先局部左旋,再整体右旋
再右旋
右右: 一次左旋
右左: 先局部右旋,再整体左旋
再左旋
1.5 红黑树
- 它是一种
特殊的二叉查找树
红黑树的每一个节点上都有存储位表示节点的颜色 - 红黑树
不是
高度平衡的,它的平衡是通过"红黑规则"进行实现的每一个节点可以是红或者黑
红黑规则:
- 每一个节点或是红色的,或者是黑色的
- 根节点必须是黑色
- 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为NiL,这些N1视为叶节点,每个叶节点(Nil)是黑色的
- 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
- 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
添加节点规则
默认颜色
: 添加节点默认是红色
的 (效率高)
因为如果是全为黑节点,要调整
两次
,如果全为红色节点,只调整一次
。
二、Set系列集合
特点:
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引: 没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
Set接口中的方法上基本上与Collection的API一致
package SetDome;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class CreatSet {
public static void main(String[] args) {
// 创建对象
Set<String> s = new HashSet<>();
//添加对象 (不能添加相同的)
s.add("张三");
s.add("历史");
boolean a = s.add("语文");
boolean b = s.add("语文");
System.out.println(a);//true
System.out.println(b);//false
System.out.println(s);//[张三, 历史, 语文]
//通过迭代器遍历
Iterator<String> it = s.iterator();
while (it.hasNext()){
String str = it.next();
System.out.println(str);
}
//通过增强For遍历
for (String s1 : s) {
System.out.println(s1);
}
//通过lambda遍历
s.forEach(str -> System.out.println(str));
}
}
2.1 HashSet
哈希值
:
- 根据hashCode方法算出来的int类型的整数
- 该方法定义在obiect类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点:
- 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
- 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
- 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
HashSet 底层原理
- HashSet集合底层采取哈希表存储数据
- 哈希表是一种对于增删改查数据性能都较好的结构
底层原理
- 创建一个默认长度16,默认加载因为0.75的数组,数组名table
- 根据元素的哈希值跟数组的长度计算出应存入的位置
int index = (数组长度-1) & 哈希值
- 判断当前位置是否为null,如果是null直接存入
- 如果位置不为null,表示有元素,则调用equals方法比较属性值
一样
:不存 ;不一样
: 存入数组,形成链表
JDK8以前
: 新元素存入数组,老元素挂在新元素下面
JDK8以后
: 新元素直接挂在老元素下面JDK8以后,当
链表长度超过8而且
数组长度大于等于64时自动转换为红黑树
注意:
如果集合中存储的是自定义对象,必须要重hashCode和equals方法
集合去重小练习
package SetDome;
//创建一个集合去存储多个学生对象,并且满足去重,属性值相同就代表相同
import java.util.HashSet;
public class HashSetRemoveChong {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("zhangsan",21);
Student s2 = new Student("lis",31);
Student s3 = new Student("wangwy",29);
Student s4 = new Student("zhangsan",21);
//创建一个集合
HashSet<Student> hs = new HashSet<>();
//添加对象
System.out.println(hs.add(s1));//true
System.out.println(hs.add(s2));//true
System.out.println(hs.add(s3));//true
System.out.println(hs.add(s4));//false
System.out.println(hs);//[Student{name = wangwy, age = 29}, Student{name = lis, age = 31}, Student{name = zhangsan, age = 21}]
}
}
2.2 LinkedHashSet
LinkedHashSet底层原理:
- 有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序
2.3 TreeSet
TreeSet特点:
- 不重复、无索引、可排序
- 可排序:按照元素的默认规则 (有小到大)排序。
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
TreeSet集合默认的规则:
- this: 表示当前要添加的元素
- o:表示已经在红黑树存在的元素
- 返回值:
负数
:认为要添加的元素是小的,存左边
正数
:认为要添加的元素是大的,存右边
0
:认为要添加的元素已经存在,舍弃
package SetDome;
/*需求:创建TreeSet集合,并添加3个学生对象学生对象属性:
姓名,年龄。
要求按照学生的年龄进行排序
同年龄按照姓名字母排列 (暂不考虑中文)
同姓名,同年龄认为是同一个人
方式一:
默认的排序规则/自然排序
student实现Comparable接口,重写里面的抽象方法,再指定比较规则
方法二:
* */
import java.util.TreeSet;
public class TreeSetDomeExercise {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("zhangsan",22);
Student s2 = new Student("zhangsan",12);
Student s3 = new Student("zhangsan",32);
Student s4 = new Student("zhangsan",29);
//创建TreeSet对象存储多个数据
TreeSet<Student> ts = new TreeSet<>();
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
System.out.println(ts);/*[Student{name = zhangsan, age = 12}, Student{name = zhangsan, age = 22},
Student{name = zhangsan, age = 29}, Student{name = zhangsan, age = 32}]*/
}
}
重写TreeSet:
//重写TreeSet
@Override
public int compareTo(Student o) {
//按照年龄的升序排序
return this.getAge() - o.getAge();
}
比较器排序:
package SetDome;
import java.util.TreeSet;
public class TreeSetDomeExerciseP {
public static void main(String[] args) {
//创建TreeSet对象存储多个数据
TreeSet<String> ts = new TreeSet<>((o1, o2) -> {
//先按照长度排
int i = o1.length() - o2.length();
//长度相同按照常规排
i = i == 0 ? o1.compareTo(o2) : i ;
return i;
});
ts.add("asd");
ts.add("as");
ts.add("bcd");
ts.add("es");
System.out.println(ts);//[as, es, asd, bcd]
}
}
2.4 综上几个集合的使用情况
1. 如果想要集合中的元素可重复
用ArrayList集合,基于数组的。(用的最多)
2. 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
用LinkedList集合,基于链表的。
3. 如果想对集合中的元素去重
用HashSet集合,基于哈希表的。(用的最多)
4. 如果想对集合中的元素去重,而且保证存取顺序用
LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet.
5. 如果想对集合中的元素进行排序
用TreeSet集合,基于红黑树。后续也可以用List集合实现排序
2.5 双列集合
特点:
- 双列集合一次需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- 键+值这个整体 我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象
三、Map
3.1 Map的常见的API
Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
方法名称 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(object key) | 判断集合是否包含指定的键 |
boolean containsValue(object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
细节:
添加
:
在添加数据的时候
如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回null。
如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。
3.2 Map遍历
第一种方式:键找值
通过获取所有键放到单列集合当中,再通过Map的get方法获取到所有对应键的值
package Maps;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapCreat {
public static void main(String[] args) {
//创建对象
Map<String,String> m = new HashMap<>();
//添加对象
m.put("hhh","lll");
m.put("lll","zzz");
m.put("xxx","aaa");
//删除
m.remove("hhh");
/* //清除
m.clear();*/
//判断是否为空
boolean empty = m.isEmpty();
System.out.println(empty);//false
//判断是否包含值
boolean result = m.containsKey("lll");
System.out.println(result);//true
//遍历对象
//首先获取到所有的键装在单列集合当中
Set<String> Keys = m.keySet();
//增强for
for (String key : Keys) {
String str = m.get(key);
System.out.println(key+"=" +str);
}
System.out.println("---------------------");
//lambda遍历
Keys.forEach(s-> {
String str = m.get(s);
System.out.println(s+"=" +str);
});
System.out.println("----------------");
//迭代器遍历
Iterator<String> i = Keys.iterator();
while (i.hasNext()){
String str1 = i.next();
String str = m.get(str1);
System.out.println(str1+"=" +str);
}
}
}
第二种方式:键值对
- 通过一个方法获取所有的键值对对象,返回一个Set集合
- 再通过获取到的Set集合中的
getKey()
和getValue()
分别来获取键和值
package Maps;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapForLam {
public static void main(String[] args) {
//创建对象,并且添加数据
Map<String,String> map = new HashMap<>();
map.put("hhh","lll");
map.put("lll","zzz");
map.put("xxx","aaa");
//Map第二个遍历方式
Set<Map.Entry<String, String>> entries = map.entrySet();
//强For
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key +"= " +value);
}
//lambda
entries.forEach(str->{
String key = str.getKey();
String value = str.getValue();
System.out.println(key +"= " +value);
});
//迭代器
Iterator<Map.Entry<String, String>> i = entries.iterator();
while (i.hasNext()){
Map.Entry<String, String> next = i.next();
System.out.println(next.getKey() + "= " + next.getValue());
}
}
}
第三种方式:Lambda
底层原理:
ForEach其实就是利用第二种方式进行遍历,依次得到每一个键和值再调用accept方法
//第三种遍历方式
map.forEach((key,value)->{
System.out.println(key +"= " +value);
});
3.3 HashMap
HashMap特点:
- HashMap是Map里面的一个实现类
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- 特点都是由键决定的:无序、不重复、无索引
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
package Maps.HashMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)
存储三个键值对元素,并遍历
要求:同姓名,同年龄认为是同一个学生*/
public class exercise1 {
public static void main(String[] args) {
//创建一个HashMap对象
HashMap<Student,String> hs = new HashMap<>();
//添加学生对象
Student s1 = new Student("zhangsan",25);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",18);
Student s4 = new Student("wangwu",18);
//添加学生对象到Map
hs.put(s1,"sichuan");
hs.put(s2,"guangzhou");
hs.put(s3,"tianjin");
hs.put(s4,"tianjin");
//遍历对象(第三种遍历方式)
hs.forEach((key,value)->{
System.out.println(key + "= " + value);
});
System.out.println(" ------------------------------");
//第一种遍历方式
Set<Student> str = hs.keySet();
str.forEach(s-> {
String ss = hs.get(s);
System.out.println(s+"="+ss);
});
//第二种遍历方式
Set<Map.Entry<Student, String>> entries = hs.entrySet();
entries.forEach(s->{
String value = s.getValue();
Student key = s.getKey();
System.out.println(key);
System.out.println(value);
});
}
}
3.4 LinkedHashMap
- 由键决定:
有序
、不重复、无索引。- 这里的有序指的是保证存储和取出的元素顺序一致
原理
:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序
3.5 TreeMap
- TreeMap跟TreeSet底层原理一样,都是红黑树结构的。
- 由键决定特性: 不重复、无索引、可排序
- 可排序:
对键进行排序
注意
:默认按照键的从小到大进行排序,也可以自己规定键的排序规则
Integer Double 默认情况下都是按照升序排列的
string 按照字母再ASCII码表中对应的数字升序进行排列
3.6 可变参数
格式:属性类型...名字
底层原理: 就是一个数组,Java自动就生成了,我们不在自己手动完成创建数组
public class Test {
public static void main(String[] args) {
int result = getsum(1);
System.out.println(result);
}
private static int getsum(int ...huanglei) {
int sum = 0;
for (int i : huanglei) {
sum += i;
}
return sum;
}
}
细节:
- 创建可变参数时,函数传入的形参只能有一个。
因为如果创建多个,那么第一个就会把所有的数据吸收。
- 有多个形参时,要把可变参数放在最后,
因为可变参数是个大胖子,会把后面传入的所有参数放在自己当中。
3.7 collections
java.util.Collections
:是集合工具类作用
: Collections不是集合,而是集合的工具类
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
//添加元素
Collections.addAll(list,"12","asd","wes","456");
// System.out.println(list);//[12, asd, wes, 456]
//打乱数据位置
Collections.shuffle(list);
// System.out.println(list);//位置随机打乱
//默认排序
Collections.sort(list);
// System.out.println(list);//[12, 456, asd, wes]
//按照我的排序规则排序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//按照长度排序,从大到小
return o2.length()-o1.length();
}
});
System.out.println(list);//[456, asd, wes, 12]
}
}
四、不可变集合
4.1 创建不可变集合
不可变集合:
就是不可以改变集合内容
,长度
运用场景:
- 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践
- 或者当集合对象被不可信的库调用时,不可变形式是安全的
简单理解
:不想让别人修改集合中的内容
书写格式:
- 在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合。
主要方法
方法名称 | 说明 |
---|---|
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K .V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
注意: 这个集合不能添加,不能删除,不能修改。