数据结构
数组
声明变量
type[] name 或 type name[]创建数组
type[] name = new type[size] 或 {value1, vaule2, …} 或 new type[]{value1, value2, …}创建未初始化数组,都是”0”值
foreach 遍历
for (type item : array)长度
数组是length属性,而String类length()方法,Collection接口中size()方法slice
Arrays.copyOfRange(int[], int from, int to)
发现from允许越界,可以是length
Arrays
Arrays.copyOfRange(int[], int from, int to)
Arrays.copyOf(int[], int new length)
1 | public static void main(String[] args) { |
Collections
static void reverse(List<?> list)
泛型类数组创建
List
for (int i=0; i<length; i++) {
list[i] = new ArrayList<>(); // 为每个元素开辟内存
}
同理,map
Map<Integer, Integer>[] map = new Map[length];
for (int i=0; i<length; i++) {
map[i] = new HashMap<>();
}
对象类数组创建
对象类数组创建 new同样是为数组开辟了内存,元素都是引用数据类型,占四个字节,存放对象的地址,元素值一开始都被初始化成了空指针
对于成员变量,系统会自动帮我们初始化
对于非成员变量来说,如果没有初始化,系统不会帮我们初始化,没有初始化的变量,使用它会报错
数组与List相互转化
数组转list
new ArrayList<>(Arrays.asList(array))
Arrays.asList(array)转成的是java.util.Arrays.ArrayList,内部的一个类
Arrays.asList(1, 2, 3); 也可以生成list
list转数组
list.toArray(array)
用array去接收,但是要保证array容量足够,如果不够的话不会把值传进去,如果array富余的话,用null填补空白
但是对于int与包装类Integer
不能使用上述方法
数组转list
int[]数组 不能使用Arrays.asList(),因为与Integer不匹配
只能遍历int[],按照各个元素添加进list
list转数组
list.toArray()不能使用int[]数组接收
可以Object[] o = list.toArray() 然后遍历的时候强制类型转换
或者使用Integer[] 数组接收
或者根据list.size()创建数组,遍历list给数组赋值
List
实现List的有ArrayList LinkedList Vector与Stack
Vector与Stack都是线程同步的,效率比较低,而ArrayList与LinkedList都是线程异步的,效率较高。
Vector使用数组实现的,Vector与Stack不再推荐使用了,因为Vector设计的效率不高,而Stack继承了Vector,是为了复用其方法,本不该继承。
List 有序 允许重复元素
Stack
Stack从逻辑上表现栈的特性,但是底层仍然是从push顺序,从前往后排列
1 | public class TestStack { |
增
push()删
pop()查
peek()
int search(Object o)
boolean empty()
ArrayList
特性
底层用数组实现,线程异步的,效率比较高,由于底层是数组,查询效率高,增删效率低构造方法
ArrayList()
ArrayList(Collection<? extends E> c)
ArrayList(int capacity)
- 扩容
当数组满了,长度会扩容到原来的1.5倍
LinkedList
- 特性
底层用双向链表实现,线程异步,效率较高,由于底层是双向链表,查询效率低,增删效率高
看javaguide发现,LinkedList在开头与末尾增删效率才高O(1),在其他位置增删,是O(n),因为需要先找到这个位置
一般需要用到LinkedList的都可以用ArrayList代替,并且性能会更好
链表比数组的优点就是开头增删是O(1),而数组O(n)
- 构造方法
LinkedList()
LinkedList(Collection<? extends E> c)
List方法
- 增
add() 默认加在末尾
add(index, element)
- 删
remove(index) remove(element)
clear()
改
set(index, element)查
get(index)遍历
foreach (type item : list) 或者利用 list.size()toString()
list的toString方法不会变成字符串,而是以列表的形式出书”[1, 2, 3]”
1 | public class TestToString { |
切片
subList(frontIndex, toIndex)
切片返回的仍然是list,但是是浅拷贝,也就是说,改动列表值会影响原来的值,可以通过
new ArrayList<>(list.subList())创建新的
与Arrays.copyOfRange()一样,fromIndex允许越界,可以是length
Map
Map的实现常用的有HashMap, HashMap的底层是数组与链表构成,线程异步的,增删改查的时间复杂度都是O(1)
HashMap允许有null键值 无序 不允许有重复键
put(key, value)
get()
containsKey(key)
- 遍历
1
2
3
4
5
6
7
8
9
10
11
12for (Map.Entry<K, V> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
for (K key : map.keySet()) {
map.get(key);
}
for (V value : map.values()) {
}
HashMap
- 构造方法
HashMap()
HashMap(capacity)
HashMap(capacity, load)
HashMap(Map<? extends K, ? extends V> m)
TreeMap
- 构造方法
TreeMap()
TreeMap(Comparator<? super K> comparator)
TreeMap(Map<? extends K,? extends V> m)
TreeMap(SortedMap<K,? extends V> m)
- 常用方法
排序的第一个entry
Map.Entry<K,V> firstEntry()
Returns a key-value mapping associated with the least key in this map, or null if the map is empty.
排序的最后一个entry
Map.Entry<K,V> lastEntry()
Returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.
// 严格大或小
lowerEntry()
higherEntry()
// 可能相等
floorEntry()
ceilingEntry()
// 小于或大于key的子map
NavigableMap<K,V> headMap(K toKey, boolean inclusive)
Returns a view of the portion of this map whose keys are less than (or equal to, if inclusive is true) toKey.
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
Returns a view of the portion of this map whose keys are greater than (or equal to, if inclusive is true) fromKey.
倒序k集合
NavigableSet
Returns a reverse order NavigableSet view of the keys contained in this map.
1 | public static void main(String[] args) { |
对map排序
TreeMap 传入comparator,只能是针对key类型的comparator
1 | import java.util.Map; |
如何实现按照value排序
想用把map传到comparator,发生了循环依赖
1 | // 因为TreeMap构造函数中接收的Comparator必须是Key类型的,本例就是必须是String类型的才行,所以为了保证String类型,想在Comparator中传入map,通过String,映射到value进行比较 |
利用Entry collections,不过发现Map.Entry 只是接口,AbstractMap.SimpleEntry才是实现类,不方便
1 | public class TestTreeMap { |
自定义entry类,需要**实现Comparable接口(必须实现,不实现接口,自己单独定义compareTo方法,调用sort报错)**,并重写compareTo方法
1 | public class TestTreeMap { |
Set
Set实现常用的有HashSet,HashSet基于HashMap实现,线程异步的
HashSet 允许有null 无序 不允许有重复元素
HashSet
- 构造方法
HashSet()
HashSet(capacity)
HashSet(capacity, load)
HashSet(Map<? extends E> c)
Set方法
增
add(e)删
remove(e)改
不支持查
不支持
可以使用contains(e) 判断是否有该元素
- 遍历
for (type item : set)
Queue
LinkedList实现
特性
底层用双向链表实现,线程异步,效率较高,由于底层是双向链表,查询效率低,增删效率高构造方法
LinkedList()
LinkedList(Collection<? extends E> c)
PriorityQueue实现
- 特性
底层实现用数组,逻辑结构是二叉堆。二叉堆是完全二叉树,要求每一个父节点比孩子节点大或小,对应的就是最大堆与最小堆。
主要有三种操作,上浮,下沉,构建。
上浮:插入在末尾,比父节点小就一直上浮
下沉:删除堆顶节点,最后的节点放到堆顶,如果父节点比最小的孩子节点小,就一直下沉。
构建:从最后一个非叶子节点开始,节点逐个一直下沉。为什么不从顶开始下沉呢,因为顶部的下沉以后,并不能保证顶是最小的,顶还需要下沉。
二叉堆是完全二叉树,用数组保存,已知父节点求下标parrent,左孩子节点是array[parent * 2 + 1] 右孩子节点是array[parent * 2 + 2]
已知孩子节点下标child,求父节点array[(child - 1) / 2]
当child 是 偶数时,右孩子,(child - 1) / 2是parent+0.5,不过由于int类型之间的运算只会取整
当child是奇数时左孩子,(child - 1) / 2恰好是parent
- 构造函数
PriorityQueue(Collection<? extends E> c)
PriorityQueue(Comparator<? super E> comparator)
Queue方法
增
add(e)删
remove()查
element()
上述方法都会抛出异常
offer() poll() peek()没有异常
PriorityQueue
boolean offer(E e)
Inserts the specified element into this priority queue.
E peek()
Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
E poll()
Retrieves and removes the head of this queue, or returns null if this queue is empty.
1 | import java.util.*; |
Deque
接口,LinkedList、ArrayDeque实现
add() 在队尾加入,也就是最后一个元素
remove()移除队头的元素,也就是第一个元素
用其当作Stack使用,提供了push与pop方法,不过,底层的顺序与逻辑顺序是一致的,栈顶就是链表的开头
- pop
removeFirst()
pop() 空的时候异常
poll() 空的时候返回null
- push
addFirst(e)
void push(e) 没有容量的时候异常
boolean offer() 没有容量的时候false
- peek
getFirst()
E element() 异常
E peek() null
Deque与Queue使用情况总结
ArrayDeque在入队时,可能会扩容,但是均摊入队复杂度还是O(1)
LinkedList在入队时,需要申请新的堆空间,复杂度比ArrayDeque高
所以还是那句话,能不用LinkedList就不用,其他用数组实现方式效率更高
Queue可以使用LinkedList,ArrayDeque,PriorityQueue实现
优先队列,使用PriorityQueue实现
普通队列使用ArrayDeque实现,能不用LinkedList就不用
Deque可以使用LinkedList,ArrayDeque实现
使用ArrayDeque实现,能不用LinkedList就不用
字符串
String
- 字符串相等比较
str1 == str2比较的是引用
str1.equals(str2)才是比较的内容
str1 = “123”
str2 = “123”
str1 == str2为true,因为相同的字符串只存在一份,引用相同
str3 = new String(str1)
str1 == str3 为false,因为引用不同
判断字符串是否为空时应该用
str == null || “”.equals(str)
- 字符串遍历
for (int i=0; i<str.length();i++) {
str.charAt(i)
}
变成字符数组
char[] toCharArray()join
static String join(CharSequence delimiter, CharSequence elements)
测试发现 elements可以传入String与String[],传入String 没有改变,传入String []可以连接
elements不能是整数列表
split
String[] split(String)replace
replace(char oldChar, char newChar)
replace(String oldStr, String newStr)
- substring
String substring(int beginIndex)
String substring(int beginIndex, int endIndex)
- indexOf
int indexOf(int ch)
int indexOf(int ch, int fromIndex)
int indexOf(String str)
int indexOf(String str, int fromIndex)
找不到返回-1
int ch 把字符使用int强制类型转成了ascii码编号
- trim
阶段开始与结束空格,返回新字符串
StringBuilder
现场不安全,效率高
- 构造函数
StringBuilder(capacity)
StringBuilder(String)
长度
长度 length()增
append(String)
append(float)
append(int)
append(char)
append(char[])
- 删
delete(start, end)
deleteCharAt(index)
改
replace(start, end, str)查
char chartAt(index)
String substring(start, end)
- 反转
StringBuilder reverse()
Causes this character sequence to be replaced by the reverse of the sequence.
String 与 数字转换
String2数字
Integer.parseInt(String)
数字2String
“” + 数字
数字.toString()
小数四舍五入
Math.round可以四舍五入为整数
格式化输出可以四舍五入为小数
1 | System.out.printf("四舍五入保留四位小数: %.4f\n", 3.14159); |
语法
java不支持逗号表达式
static
static 修饰方法、属性或者代码块
static修饰的方法,在类加载的初始化;
修饰的属性,在类加载的时候初始化,且只有一份;
修饰的代码,在类加载的时候执行,只执行一次;
- static修饰的方法
static修饰的方法,可以在类没有创建的时候调用该方法
静态方法中不可以出现非静态成员,但是非静态方法中可以出现静态成员
静态成员被类共有,只有一份,但是也属于实例对象,也就是说在非静态方法中可以通过this访问静态属性或者静态方法
static修饰的属性
只有一份,属于类也属于实例对象,可以通过this访问static修饰的代码块
类加载时执行
final
被final修饰的变量(成员变量、非成员变量),在初始化后就不能改变了
可以在声明的时候初始化,也可以之后再初始化
final修饰的成员变量如果声明的时候没有初始化,必须在构造函数中初始化
类加载与对象实例化的执行顺序
类加载
先加载父类,再加载子类,加载的时候执行静态操作实例化
先实例化父类,再实例化子类,实例化父类的时候,先初始化父类非静态属性,然后执行父类构造器;然后初始化子类非静态属性,执行子类构造器。
&的结合优先级没有 == 优先级高
2&1 == 1 表达的意思是2 & 1==1,要想用位与的方式判断2是否是奇数,应该加括号
(2&1) == 1
局部变量重复定义
如下代码,java报错a重复定义,但是cpp不报错。如果之前在上级作用域声明过变量,不能在下级作用域再次定义,而如果把int a=1;放到while循环之后则可以运行,while循环中的int a=2失效了
1 | int duplicate() { |
Comparator
Comparator 有两个抽象方法需要实现,equals与compare,其他都是静态方法,无需实现
由于每个类都会默认继承Objects,Objects中有equels方法,所以无需实现equals,只用实现compare就行了
1 | public class MyComparator (extends Objects) implements Comparator<String> { |
sort(T[] a, Comparator<? super T> c),不能排序int
java格式化输出
https://blog.csdn.net/qq_44111805/article/details/112850550
https://blog.csdn.net/weixin_43388956/article/details/123072313
1 | System.out.printf("一个字符串:%s,一个浮点数:%f,一个整数:%d",str,pi,i); |
== 与 equals
== 比较的肯定是变量的值,基本数据类型存放的就是值本身,引用数据类型存放的是地址值,==在比较的时候就是比较引用数据类型存放的地址值
equals在Object类中,就是利用==比较的地址值,但是一般需要重写,让HashMap调用,因为我们是当key指向的对象的值相等时,发生了冲突,而不是地址
1 | public boolean equals(Object obj) { |
hashCode()的作用
- hashCode()计算哈希码,一个整数值,确定索引的位置这是O(1)查找的关键
- 如果没有hashCode(),只有equals(),判断是否重复的话,只能对哈希表中的所有数据都equals()判断。而有了hashCode()之后,只会对碰撞后的元素进行equals(),提高了运行速度
- 重写equals() 一定要重写hashCode(),因为把对象加入哈希数据结构中时,首先会使用hashCode()计算哈希码,根据哈希码计算索引值,即加入的位置。如果该位置有其他对象,就与其他对象比较哈希码是否相等,如果不相等,就认为对象不相等,新加入的对象追加到该位置的后面。如果相等,由于哈希碰撞问题的存在,还要通过equals()方法比较值是否真的相同。 从加入过程可以看到,先比较hashCode(),再比较equals(),该规则已经默认了equals()相同的元素,hashCode()也应该相同。但是hashCode()默认是根据地址值计算的,需要重写为,equals()比较时使用的值是什么,计算哈希码时也要使用该值。
精简版解释:hashCode() 与 equals()默认都是用地址值计算,如果重写equals(),而不重写hashCode(),hashCode()仍然使用地址值计算。而在哈希表读写时,会先调用hashCode()方法,计算哈希码,这样对于值相同,但是地址值不同的对象,hashCode()计算的结果不同,会认为这两个对象不同,但是按照equals()的逻辑,这两个对象其实是相同的。
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashCode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode 值作比较,如果没有相符的 hashCode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashCode 值的对象,这时会调用 equals() 方法来检查 hashCode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
泛型
泛型接口实现
1 | public class TestT { |
泛型类
1 | public class GrandFather<T> {}//祖父类 |