public class test {
//初始化块
{
a = 5;//a 的类型和a的信息,是在编译的时候确定了(先执行)
System.out.println("代码块1");
}
public int a = 10;后执行
public int a = 10;前执行
//交换位置
{
a = 5;//a 的类型和a的信息,是在编译的时候确定了(先执行)
System.out.println("代码块1");
}
(结果为5)
public test(){
System.out.println("无参构造");
}
{
System.out.println("代码块2");
}
public static void main(String[] args) {
/*Person person = new Person();
System.out.println(person.name);
System.out.println(Person.length);*/
test test = new test();
System.out.println(test.a);
//使用静态方法时不会受到空指针影响
//131* 289(bow)
//罗技()
//Person person = new Person();
//在堆内存中生成的new Person();对象
/*person.name = "dang";
person.age="22";
person.hello();*/
/*Person person1 = person;
//person1的指针指向的便是person对象
person1.name = "123";
//因此修改person的对象的数据也就是修改person1的对象的数据
System.out.println(person.name+"---"+person1.name);*/
}
}
上述结果为a=10; 说明: 初始化块时,变量a放在代码块后面就后执行,放在前面就在前面执行。
静态初始化块
{
System.out.println("代码块1");//2
}
public test(){
System.out.println("无参构造");//3
}
static {
System.out.println("代码块2");//1
}
Lambda表达式
public static void main(String[] args) {
VMath va = (int... nums) -> {
int resilt = 0;
for(int num : nums){
resilt = resilt+ num;
}
return resilt;
};
System.out.println(va.addAllNum(1,2,3));
}
byte a = 56;//表述范围小的可以转换为表述范围大的
int b = a;
System.out.println(b);
输出为56
int a = Integer.MAX_VALUE;
int b = a+1;
System.out.println(b);//-2147483648
System.out.println(b > a);//false
对比
int a = Integer.MIN_VALUE;
int b = a-1;
System.out.println(b);//2147483647
System.out.println(b > a);//false
转换
int a = 200;
byte d = (byte) a;//a(200)的数值大小超过了byte的数值范围,因此在强制转换过程中出现失误
System.out.println("d"+d);//-56(错误)
short a = 100;
short b = a - 3;//short是范围小的转换为-3为范围大的,因此需要对其进行强制转移
枚举
public enum SessionEnum {
SPRING, SUMMMER, FALL, WINTER;
public String name;
}
//遍历枚举的数值
for(SessionEnum sessionEnum : SessionEnum.values()){
System.out.println(sessionEnum);
}
// A.compareTo(B)若A在B前面定义,那么返回-1,若在B之后定义返回1
System.out.println(SessionEnum.SUMMMER.compareTo(SessionEnum.FALL));
//获取实例名称
//获取实例的序列顺序
System.out.println(SessionEnum.SUMMMER.ordinal());
//获取对应类型,指定名字的枚举:
SessionEnum sessionEnum1 = Enum.valueOf(SessionEnum.class, "SPRING");
sessionEnum1.name = "春天";
System.out.println(sessionEnum1);
SessionEnum sessionEnum = SessionEnum.FALL;
switch (sessionEnum){
case SPRING:
System.out.println("春天");
break;
case SUMMMER:
System.out.println("夏天");
break;
case FALL:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
default:
System.out.println("季节错误");
break;
}
枚举构造函数
public enum SessionEnum {
SPRING("春天"), SUMMMER("夏天"), FALL("秋天"), WINTER("冬天");
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
SessionEnum(String name){
setName(name);
}
}
实现接口的枚举类
package com.dang;
import org.apache.kafka.common.protocol.types.Field;
/**
* @author 16842
* @version 1.0.0
* @description TODO
* @date 2022/8/29 22:02
*/
public enum SessionEnum implements VMath{
SPRING("春天"){
@Override
public void info() {
System.out.println("表示春天");
}
},
SUMMMER("夏天"){
@Override
public void info() {
System.out.println("表示夏天");
}
}, FALL("秋天"){
@Override
public void info() {
System.out.println("表示秋天");
}
}, WINTER("冬天"){
@Override
public void info() {
System.out.println("表示冬天");
}
};
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
SessionEnum(String name){
setName(name);
}
@Override
public void info() {
System.out.println("这是一个定于季节的枚举类");
}
}
包含抽象方法和静态方法
public enum Operation {
//第一行放所有的枚举实例
PLUS{
@Override
public double eval(double a, double b) {
return a + b;
}
};
//抽象方法
public abstract double eval(double a,double b);
public static void main(String[] args) {
System.out.println(Operation.PLUS.eval(2,5));
}
}
垃圾回收
垃圾回收机制要注意以下几点:
(1),垃圾回收机制只负责回收内存中的对象,但是无法回收物理资源(数据库连接,网络IO等)
(2),程序无法精确的控制垃圾回收的运行,垃圾回收会在任何时候进行。当对象永久性的失去引用后,系统就会在任何的时候回收它所占的内存
(3),在垃圾回收机制回收任何对象之前,总会调用他的finalize()方法。
(1),可达状态:当一个对象被创建后,若有一个以上的引用变量引用它,则这个对象在程序中处于可达状态,程序可通过引用变量来调用该对象的实例变量和方法。 (2),可恢复状态:如果程序中某个对象不再有任何对象引用它,它就进入了可恢复状态。在这个情况下,系统的垃圾回收机制准备回收该对象占用的内存,在回收该对象之前,系统会调用可恢复状态对象的finalize()方法来进行资源清理。如果系统在调用finalize()方法的时候重新让一个引用变量引用了该对象,那么这个对象会再次变为可达状态。否则对象进入不可达状态。 (3),不可达状态:当对象所有引用变量的关联都被切断,并且重新调用了finalize()方法之后,还是没有变成可达状态,那个这个对象就变成不可达状态,系统就会开始回收资源
强制垃圾回收
public class test {
private String name;
public test(String name){
this.name = name;
}
//垃圾回收
@Override
protected void finalize() throws Throwable {
System.out.println(name+"被回收了");
super.finalize();
}
public static void main(String[] args) {
//垃圾回收
for(int i = 0;i<10;i++){
new test("name"+i);
System.out.println("循环打印"+i);
System.gc();//强制垃圾回收
Runtime.getRuntime().gc();
}
}
Runtime使用
Runtime runtime = Runtime.getRuntime();
//处理器数量
try {
//使用exec开启桌面客户端
runtime.exec("E:\Sublime\Sublime Text 3x64\sublime_text.exe");
} catch (IOException e) {
e.printStackTrace();
}
Object类使用
public class VmLambba {
public static class Address{
String detail;
public Address(String detail){
this.detail = detail;
}
}
private static class User implements Cloneable{
private int age;
private Address address;
public User(int age){
this.age = age;
this.address = new Address("四川");
}
//实现克隆函数
@Override
protected User clone() throws CloneNotSupportedException {
return (User)super.clone();
}
}
public static void main(String[] args) {
try {
User user1 = new User(10);
User user2 = user1.clone();
System.out.println(user1 == user2);
System.out.println(user1.address == user2.address);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
空指针操作Objects类
User user1 = null;
System.out.println(Objects.hashCode(user1));//结果为0
System.out.println(Objects.toString(user1));//结果为null
User u = Objects.requireNonNull(user1,"不为空");//报错空指针异常,提示信息
System.out.println(u);
//帮助在多线程下进行实现随机数的生成,防止多线程抢占资源
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
System.out.println(threadLocalRandom.nextInt());
//不丢失精度的小数计算
BigDecimal a = new BigDecimal("0.05");
BigDecimal b = new BigDecimal("0.01");
System.out.println(a.add(b));
//日期操作
Date date = new Date();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(dateFormat.format(date));
String datetime = "2022-08-30 08:57:14";
Date date1 = null;
try {
date1 = dateFormat.parse(datetime);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(date1);
扩容机制
int[] a = new int[4];
a[0] = 0;
a[1] = 1;
a[2] = 2;
a[3] = 3;
int[] b = Arrays.copyOf(a,10);
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}
System.out.println("---------------");
System.arraycopy(a,2,a,3,3);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
HashSet底层
- 首先获取元素的哈希值,对哈希值进行运算,得出一个索引值,即为要存放在哈希表中的位置。
- 如果该位置上没有其他的元素,则可以直接存放。
1.hashmap构造器
public HashSet() {
map = new HashMap<>();
}
2.执行add方法
public boolean add(E e) {//PRESENT是hashset的静态占位的Object数组
return map.put(e, PRESENT)==null;
}
//进入hash求哈希值,得到hash(key)key对应的哈希值
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
//无符号右移16位(获取哈希值)
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量
//如果当前的table是null,或者大小为0,那么我们进行第一次扩容操作。
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据key得到的哈希值,去计算该key应该存放到taable表的哪个位置
//(2)判断p是否为null,为null,则表示没有存放过元素,存入元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {//如果已经存在相同的元素了,我们进行
add("java");
add("java");
//1.开发技巧提示:定义变量定义好,在需要的地方进行设计
//2.p.hash == hash----如果当前的第一个元素与要添加的key=的hash值一样
//并且满足准备加入的key 和 p 指向的Node节点的key是同一个对象。
//p指向的Node节点的key的equals方法的数值相同
//相同便不能加入该数值
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//p 是不是一颗红黑树
//如果是一颗红黑树,调用putTreeVal进行实现添加
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//声明链表操作
//fo循环进行比较数组的延伸链表是否有我要加入的元素。有的话直接走人。
for (int binCount = 0; ; ++binCount) {
//(1)依次和该链表的每个元素都比较进行向后走,则加入到该链表的最后,然后break
if ((e = p.next) == null) {
//尾插法添加链表节点
p.next = newNode(hash, key, value, null);
//注意在把元素添加到链表,该链表是否右8个节点
//如果达到8个链表节点,执行红黑树的生成
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//(2)如果存在相同的元素直接break
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;//推出后完成p = e的赋值
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;//修改次数+1
if (++size > threshold)//当前的数据量是否大于12
resize();
afterNodeInsertion(evict);//扩容
return null;
}
newCap = DEFAULT_INITIAL_CAPACITY;//16个大小
//用了12个空间的时候考虑进行扩容
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
&运算符使用
`红黑树
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();//如果节点数目达到大于8个,生成红黑树
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
HashSet源码实现add元素流程
graph TD
底层是HashMap --> 通过hashCode生成元素的哈希值 --> putVal函数处理 --> 找到存储数据表table --> 没有元素为null --> resize函数
-
在扩容后,通过 -
(p = tab[i = (n - 1) & hash]完成获取该位置是否有元素,没有元素直接加入该元素tab[i] = newNode(hash, key, value, null);加入后执行返回null。,则表示添加成功 -
若是添加已经存在的元素,大致流程如下:
-
首先是
(p = tab[i = (n - 1) & hash])这个地方已经存在的该元素了,则一定不为null -
其次调用equals
((k = p.key) == key || (key != null && key.equals(k)))进行添加元素的数值的进行比较,如果相等则让数组延伸的链表进行p = e; -
相等后进行for无线循环遍历链表节点,
-
1.依次遍历到链表末尾都没有找到该重复元素,那么末尾直接插入,然后break;
-
2.找到了相同元素直接break;
-
每次遍历一个链表节点,bigCount+1,超过8则生成红黑树
-
这次不返回null,因此该操作直跳过,这也就是Set为什么能保证元素唯一的实现方式。
分析HashSet的扩容和转成红黑树机制
- 底层是HashMap,第一次添加时table的数组的容量为16,临界值(threshlod)为16 * 加载因子(0.75)= 12
- 如果table使用到了临界值12,就会扩容到16 * 2 = 32,新的临界值就是32 * 0.75 = 24
- 如果一个链表的数据个数达到8个,当前的table大小不足64的话,会首先进行扩容,也就是数组大小由16---> 32,然后继续假如元素,然后进行再加一次变为64
- 并且table的大小 >= 64,那么便会进行红黑树的转化
- 由Node变为TreeNode操作
HashSet实战
设计一个Employee类,实现相同名称和年龄的员工无法加入HashSet
- 正常创建的类
class Employee{
private String name;
private int age;
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + "]";
}
//有参构造
public Employee(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 正常的HashSet添加
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet hsSet = new HashSet();
hsSet.add(new Employee("jack",18));
hsSet.add(new Employee("tom",28));
hsSet.add(new Employee("jack",18));
System.out.println("hsset内容"+hsSet);
}
结果为:
hsset内容[Employee [name=jack, age=18], Employee [name=tom, age=28], Employee [name=jack, age=18]]
很显然并没有完成添加、
基于HashSet的添加机制,首先是根据hash值获得索引值,索引值没有元素加入,有元素继续比较,若equals相同则直接返回,不同则加入到链表尾部
因此关键在于hash和equal我们重写这两个方法
class Employee{
private String name;
private int age;
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + "]";
}
//有参构造
public Employee(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
//重写equal方法和hashCode方法,如果name和age相同则返回相同的hash数值和equals结果
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
测试结果:
hsset内容[Employee [name=tom, age=28], Employee [name=jack, age=18]]
完成添加
LinkedHashSet分析
- LinkedHashSet的底层维护的是一个LinkedHashMap结构,LinkedHashSet是数组和双向链表进行实现,能够实现根据双向链表遍历所有的添加数据
- 第一次添加元素时,table直接扩容到16,放入的节点是LinkedHashMap$Entry,(Entry是継承HashMap)
- 数组是HashMap$Node[] 存放的数据是 Linked
Map练习
创建一个员工类,(id,name,salary) 采用两种方式遍历工资大于18000的员工的个人信息
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Person {
public static void main(String[] args) {
Map<Integer,Employyee> map = new HashMap<>();
map.put(001,new Employyee(001,"李明",new BigDecimal(5000.00)));
map.put(002,new Employyee(002,"赵四",new BigDecimal(182000.00)));
map.put(003,new Employyee(002,"王五",new BigDecimal(188000.00)));
map.put(004,new Employyee(003,"赵六",new BigDecimal(199000.00)));
//遍历使用entryset遍历
Set enyset = map.entrySet();
for(Object o : enyset){
Map.Entry m = (Map.Entry) o;
Employyee test = (Employyee) m.getValue();
int flag = test.getSalary().compareTo(new BigDecimal(18000));
if(flag == 1){
System.out.println(test.getId()+test.getName()+test.getSalary());
}
}
//使用迭代器
Iterator iterator = enyset.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
//System.out.println(o.getClass());//HashMap$Node类型,同样也是实现了Entry
Map.Entry m = (Map.Entry) o;
Employyee test = (Employyee) m.getValue();
int flag = test.getSalary().compareTo(new BigDecimal(18000));
if(flag == 1){
System.out.println(test.getId()+test.getName()+test.getSalary());
}
}
}
}
class Employyee{
private int id;
private String name;
private BigDecimal salary;
public Employyee(int id, String name, BigDecimal salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getSalary() {
return salary;
}
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
}
HashMap与HashSet一样不保证顺序的存储操作
HashTable的介绍使用
- hashtable的键和值都不能为null,否则NullPointException
- HashTable是线程安全的,hashMap是线程不安全的
HahsTable的初始化的含量为11,临界因子0.75,临界值为8
public Hashtable() {
this(11, 0.75f);
}
hashTable扩容代码:
Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
int newCapacity = (oldCapacity << 1) + 1;
新的容量是原来容量2 +1,
同时临界值也是扩容容量0.75
TreeSet解析
- 使用无参构造TreeSet创建时,TreeSet还是无序的 2.通过比较器完成排序操作
/*String a = "ab";
String b = "ba";
System.out.println(a.compareTo(b));//结果-1*/
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//根据ASCII码进行字符串的判断
return ((String)o1).length() - ((String)o2).length() ;
}
});
//按照字符串大小来排序(字母表abcdefg)
//使用TreeSet提供的构造器,传入一个比较器
treeSet.add("jack");
treeSet.add("tom");
treeSet.add("lisa");
treeSet.add("a");
//排序结果:TreeSet[a, jack, lisa, tom]
System.out.println("TreeSet"+treeSet);
TreeSet源码
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
//compare调用的构造器中写的接口类
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else//如果相等,则数据是加不进去的,与HashSet的添加机制相同
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
//内部类
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
根据上述的比较器的比较规则:
return ((String)o1).length() - ((String)o2).length() ;
同时根据TreeSet源码
//compare调用的构造器中写的接口类
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else//如果相等,则数据是加不进去的,与HashSet的添加机制相同
return t.setValue(value);
因此对于TreeSet里面的数据在长度相同时是无法加入的,
原因在于,当前的compare比较结果为0是,是返回一个数值,但是add调用的函数底层是如下的形式:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
因此返回的数值不为null,所以加不进去相同长度的字符串数据。
TreeMap解析+源码
- 首先TreeMap和HashMap一样,键值对中,value可以为null,但是key不可以为null
- Treemap与TreeSet的底层区别是:
TreeSet的底层便是TreeMap,比较器的代码一摸一样,但是TreeSet在比较器结果返回为0时,是返回比较数据,如果比较数据不为null,那么该数据是无法加入TreeSet的,但是TreeMap则是在原来的基础上对该key对应的value进行覆盖。 代码如下:
- TreeSet
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//根据ASCII码进行字符串的判断
return ((String)o1).length() - ((String)o2).length() ;
}
});
//按照字符串大小来排序(字母表abcdefg)
//使用TreeSet提供的构造器,传入一个比较器
treeSet.add("jack");
treeSet.add("tom");
treeSet.add("lisa");//无法加入,因为长度与jack相同
treeSet.add("a");
//排序结果:TreeSet[a, tom,jack]
System.out.println("TreeSet"+treeSet);
2.TreeMap
TreeMap treeMap = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//长度进行比较
return ((String)o1).length() - ((String)o2).length() ;
}
});
treeMap.put("jack","123");
treeMap.put("tom", null);//正常加入
treeMap.put("tom", "123");//此时tom的value被修改为123,也就是tom->123
treeMap.put("abc", null);//由于abc与tom字符串长度相等,则tom的键值对value被再一次的修改,得到tom->null
//结果{tom=null, jack=123}
System.out.println("treemap"+treeMap);
根据上述的代码TreeMap的实现与TreeSet的实现有一定的区别
Collections工具类使用
List list = new ArrayList();
list.add("123");
list.add("23456");
Collections.sort(list, new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
return ((String)o2).length() - ((String)o1).length();
}
});
System.out.println(list.toString());
集合测试题
1.Person;类完成了对id和name的hashCode和equals方法的重写操作
class Person{
public int id;
public String name;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
HashSet<Person> hashSet = new HashSet<Person>();
Person p1 = new Person(1001, "AA");
Person p2 = new Person(1002, "BB");
hashSet.add(p1);
hashSet.add(p2);
p1.name = "CC";
hashSet.remove(p1);
//1.输出结果为
System.out.println(hashSet);
hashSet.add(new Person(1001, "CC"));
//2.输出结果为
System.out.println(hashSet);
hashSet.add(new Person(1001, "AA"));
//3.输出结果为:
System.out.println(hashSet);
}
分析:
-
第一次的输出结果 `HashSet hashSet = new HashSet(); Person p1 = new Person(1001, "AA");//ok Person p2 = new Person(1002, "BB");//ok hashSet.add(p1); hashSet.add(p2);
p1.name = "CC";当前p1的name为CC hashSet.remove(p1); //1.输出结果为 System.out.println(hashSet);`
当前在hashset的数组布局中,
因此在执行remove操作的时候,需要先去查找以p1(1001,"CC")的索引位置,因此我们通过查找没有发现任何的元素,因此remove无效,因此当前的hashset的位置结果输出为:
1002BB 1001CC
2.hashSet.add(new Person(1001, "CC")); //2.输出结果为 System.out.println(hashSet);
根据(1001,"CC")的hash索引值的计算,当前的位置没有元素存在,因此结果为:
hashSet.add(new Person(1001, "AA")); //3.输出结果为: System.out.println(hashSet);
该行代码,由于(1001,"AA")的元素已经存在了一个p1(1001,"CC"),但是由于当前的元素只是hash索引值相同但是对于hash的key以及equals不同,因此我们对其进行直接插入到该p1的后面如下:
集合扩容
- ArrayList,有参构造,初始为有参容量,然后是扩容到1.5倍。午餐构造,初始为10,然后是1.5倍
- Vector,初始为10,然后是2倍扩容
- LinkedList双向链表的添加
- HashSet初始化为16,底层是HashMap,第一次添加时table的数组的容量为16,临界值(threshlod)为16 * 加载因子(0.75)= 12,如果table使用到了临界值12,就会扩容到16 * 2 = 32,新的临界值就是32 * 0.75 = 24如果一个链表的数据个数达到8个,当前的table大小不足64的话,会首先进行扩容,也就是数组大小由16---> 32,然后继续假如元素,然后进行再加一次变为64,并且table的大小 >= 64,那么便会进行红黑树的转化,由Node变为TreeNode操作。
- LinkedHashSet,初始化为16,
- HashMap初始化为为16,然后当链表元素到达8 数组达到64进行树化
- HashTable初始化为11,0.75临界银子,新的容量是原来容量2 +1,同时临界值也是扩容容量0.75
页面静态化
-
页面缓存
-
用途:对于一些不怎么有变动的页面,比如用户和专家列表页面的实现,同时访问量也挺多的页面
-
实现:首先查询缓存,若缓存中不存在则进行手动渲染,手动渲染通过thymeleafViewRevlorer进行模板渲染,而后通过SpringWebCOntext进行数据的上下文获取,通过网页的渲染,将其网页的静态数据加载到缓存中,下次直接从缓存中获取,减轻压力。
-
URL缓存
-
用途:针对不同的用户信息和专家信息有不同的页面详情,根据id进行区分,实现原理相同。
-
对象缓存,基于相关的缓存操作进行实现,
hash值的计算方式
简单来说就是: hash等于hashcode的数值计算后高16位不变,低16位与高16位进行异或运算(相同为0,不同为1),然后与(n-1)进行与运算进行获得索引值