Java基础学习,简单整理

80 阅读15分钟
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()方法。

image.png

(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();
        }

image.png

空指针操作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);

&运算符使用

image.png

`红黑树

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

  1. 正常创建的类
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;
	}
	
}
  1. 正常的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分析

  1. LinkedHashSet的底层维护的是一个LinkedHashMap结构,LinkedHashSet是数组和双向链表进行实现,能够实现根据双向链表遍历所有的添加数据
  2. 第一次添加元素时,table直接扩容到16,放入的节点是LinkedHashMap$Entry,(Entry是継承HashMap)
  3. 数组是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的介绍使用

  1. hashtable的键和值都不能为null,否则NullPointException
  2. 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解析

  1. 使用无参构造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解析+源码

  1. 首先TreeMap和HashMap一样,键值对中,value可以为null,但是key不可以为null
  2. Treemap与TreeSet的底层区别是:

TreeSet的底层便是TreeMap,比较器的代码一摸一样,但是TreeSet在比较器结果返回为0时,是返回比较数据,如果比较数据不为null,那么该数据是无法加入TreeSet的,但是TreeMap则是在原来的基础上对该key对应的value进行覆盖。 代码如下:

  1. 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);
	}

分析:

  1. 第一次的输出结果 `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的数组布局中,

hashset.png

因此在执行remove操作的时候,需要先去查找以p1(1001,"CC")的索引位置,因此我们通过查找没有发现任何的元素,因此remove无效,因此当前的hashset的位置结果输出为:

1002BB 1001CC

2.hashSet.add(new Person(1001, "CC")); //2.输出结果为 System.out.println(hashSet);

根据(1001,"CC")的hash索引值的计算,当前的位置没有元素存在,因此结果为:

hashset.png

hashSet.add(new Person(1001, "AA")); //3.输出结果为: System.out.println(hashSet);

该行代码,由于(1001,"AA")的元素已经存在了一个p1(1001,"CC"),但是由于当前的元素只是hash索引值相同但是对于hash的key以及equals不同,因此我们对其进行直接插入到该p1的后面如下:

hashset.png

集合扩容

  1. ArrayList,有参构造,初始为有参容量,然后是扩容到1.5倍。午餐构造,初始为10,然后是1.5倍
  2. Vector,初始为10,然后是2倍扩容
  3. LinkedList双向链表的添加
  4. 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操作。
  5. LinkedHashSet,初始化为16,
  6. HashMap初始化为为16,然后当链表元素到达8 数组达到64进行树化
  7. HashTable初始化为11,0.75临界银子,新的容量是原来容量2 +1,同时临界值也是扩容容量0.75

页面静态化

  1. 页面缓存

  2. 用途:对于一些不怎么有变动的页面,比如用户和专家列表页面的实现,同时访问量也挺多的页面

  3. 实现:首先查询缓存,若缓存中不存在则进行手动渲染,手动渲染通过thymeleafViewRevlorer进行模板渲染,而后通过SpringWebCOntext进行数据的上下文获取,通过网页的渲染,将其网页的静态数据加载到缓存中,下次直接从缓存中获取,减轻压力。

  4. URL缓存

  5. 用途:针对不同的用户信息和专家信息有不同的页面详情,根据id进行区分,实现原理相同。

  6. 对象缓存,基于相关的缓存操作进行实现,

hash值的计算方式

image.png

简单来说就是: hash等于hashcode的数值计算后高16位不变,低16位与高16位进行异或运算(相同为0,不同为1),然后与(n-1)进行与运算进行获得索引值