一、框架
在程序中,将一些功能封装,提供给其他人使用的功能叫做框架。其作用是用来简化开发过程以及代码。
二、集合
实现了类似数组的功能,将多个对象进行操作的方法进行了定义和封装,在开发时只需要调用即可,简化了数组的使用。
与数组的区别:
- 数组长度固定,集合长度不固定。
- 数组可以存储基本数据类型和引用数据类型,而集合只能存储引用数据类型。
注意:如果集合存储基本数据类型,会自动转换成包装类。
三、集合框架的体系
集合框架常用的主要有两个体系:Collection和Map
3.1 Collection体系
Collection作为整个体系的父接口,能够存储多种类的对象,特点是无序,无下标,可迭代每个元素。其中有两个常用的子接口。
- List,该接口增加下标功能,特点是有序,有下标,且元素可重复。常用实现类如下:
- ArrayList,底层使用数组实现
- LinkedList,底层使用链表实现
- Set,该接口特点是无序,无下标,且元素不能重复。
- HashSet,特点是使用HashMap的key实现。
- TreeSet,特点是使用tree实现,所以可以排序。
3.2 Collection体系常用方法
add: 添加对象
addAll: 将另一个集合中的所有对象添加到当前集合中。
clear: 清空集合中所有的对象。
contains:判断集合是否包含某个对象
isEmpty:判断集合是否为空
remove:删除一个对象。
size: 得到集合中元素的个数。
toArray:将集合转换成数组。
四、List接口相关
4.1 基本特点
List接口继承自Collection接口,特点是:有序、有下标、元素可以重复,相当于Collection接口,添加一些与下标相关的方法。
add(index,obj): 向指定下标添加元素。
get(index): 获得指定下标的元素。
4.2 有序,无序,排序
有序是会记住元素添加时的顺序存放。
无序是指添加元素的顺序与最终存放的顺序无关,顺序与用户的理解也无关。
排序是指可以根据程序员要求的特点来进行排序。
4.3 常用实现类
ArrayList (Vector) 、LinkedList
Vector与ArrayList具备几乎相同的结构以及完全相同的用法,区别在于ArrayList不安全,Vector是线程安全的,但是性能非常低下,几乎不会被使用,后面会有其他的性能相对较高的安全的集合来代替。
4.4数组和链表
- 数组的特点:连续空间,长度固定。增删慢,遍历速度较快。
- 链表的特点:随机空间,长度不固定,增删快,遍历慢。
- 链表如果操作首尾,比操作中间更快。
- 链表有单向和双向之分,单向只能向一个方向遍历,双向可以向两边遍历。
4.5 ArrayList的用法
public class TestMain1 {
//ArrayList用法
//使用数组原理实现的集合
public static void main(String[] args) {
//创建一个集合
ArrayList list = new ArrayList();
//添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//通过下标获取元素
System.out.println(list.get(2));
//通过下标遍历元素
//size方法获得集合中元素的个数
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//通过foreach遍历
for (Object object:list) {
String temp = (String) object;
System.out.println(temp);
}
//使用迭代器循环
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String next = (String) iterator.next();
System.out.println(next);
}
//删除
list.remove(1);//通过下标删除
list.remove("d");//直接删除元素
Object[] objects = list.toArray();//将集合转换成数组
}
}
注意:Vector与ArrayList用法完全相同。
4.6 LinkedList的基本用法
public class TestMain2 {
//ArrayList用法
//使用数组原理实现的集合
public static void main(String[] args) {
//创建一个集合
LinkedList list = new LinkedList();
//添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//通过下标获取元素
System.out.println(list.get(2));
//通过下标遍历元素
//size方法获得集合中元素的个数
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//通过foreach遍历
for (Object object:list) {
String temp = (String) object;
System.out.println(temp);
}
//使用迭代器循环
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String next = (String) iterator.next();
System.out.println(next);
}
//删除
list.remove(1);//通过下标删除
list.remove("d");//直接删除元素
Object[] objects = list.toArray();//将集合转换成数组
//除了上面通用的用法,还有一些特殊的用法
list.addFirst("1");//在链表最前面添加
list.addLast("2");//在链表最后面添加
list.removeFirst();//删除最前面的元素
list.removeLast();//删除最后面的元素
}
}
4.7 ArrayList底层实现
1、当创建一个无参的对象时,默认数据为一个空数组,长度为0
2、当创建的无参对象(空数组)第一次添加元素时,会将长度指定为10,添加元素时,会进行一次扩容
3、如果元素的个数没有超过数组的空间时,不会扩容。如果添加的下一个元素超过了数组空间大小时,会进行扩容。注:说明扩容的临界点是当数组元素已满时进行。
4、当进行扩容时,会扩容原来大小的1.5倍。如果扩容1.5倍时超出了范围,扩容1个空间,保障至少能添加元素。
5、当新的空间大小超出MAX_ARRAY_SIZE时,指定为Integer.MAX_VALUE,如果超过了Integer.MAX_VALUE时,则报错,
//默认大小
private static final int DEFAULT_CAPACITY = 10;
//存储数据的
Object[] elementData;
//数组中元素的数量
private int size;
//无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//通过指定长度来创建集合
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//通过指定长度来创建数组
this.elementData = new Object[initialCapacity];
//如果长度等于0,就创建一个空数组
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
//如果是负数就报错。
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
//将新添加的元素赋值到数组中,并size加1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//当通过无参构造方法创建对象数组时,默认为空数组时
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//将数组大小设置为10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//修改次数
// overflow-conscious code
//当数组元素已经达到数组所容纳的元素上限时
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
// overflow-conscious code
//旧的空间大小,原来数组的长度
int oldCapacity = elementData.length;
//新的空间大小,原来数组的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//当新的空间-需要的最小空间(size+1)小于0时,新空间直接赋值为最小空间(size+1)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//当新的空间如果大于2147483639时
if (newCapacity - MAX_ARRAY_SIZE > 0)
//指定新的空间为巨大的空间
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//创建一个新的大小数组,并将原来的数据复制到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
//保障的最小空间都超出了范围,此时直接报错
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果需要保障的最小空间2147483639,直接指定大小为最大值(2147483647),否则指定为最大数组大小。
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
4.8 LinkedList的原理
1、LinkedList是一个双向链表
2、内置一个Node节点类,来进行链接
3、会保存链表的首尾节点
4、添加元素时,会将元素保存为一个节点(Node),并设置节点相应的前后节点,以及根据情况修改LinkedList的首尾节点。
//LinkedList的内部类,用来保存一个节点
private static class Node<E> {
E item;//元素
Node<E> next;//下一个节点
Node<E> prev;//上一个节点
//有参构造方法,每次创建对象时必须指定上一个节点,当前元素,下一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
transient Node<E> first;//第一个节点
transient Node<E> last;//最后一个节点
// 无参构造方法
public LinkedList(){
}
//添加,默认在尾部添加
public boolean add(E e) {
linkLast(e);
return true;
}
//
void linkLast(E e) {
//记住最后一个节点
final Node<E> l = last;
//创建一个新的节点,将上一个节点指定为原来的最后一个节点,下一个节点指定为null
final Node<E> newNode = new Node<>(l, e, null);
//将自己指定为当前LinkedList的尾部(最后一个节点)
last = newNode;
//如果上一个节点为空,则当前节点也设置为第一个节点(首部)
if (l == null)
first = newNode;
else
//如果上一个节点不为空,则将原来的最后一个节点的下一个节点指定为当前节点
l.next = newNode;
size++;
modCount++;
}
五、泛型的使用
5.1泛型
泛型时对类型的一种约束。作用是在使用时会自动转成相应的类型,还会对不是该类型的地方进行检查,会有相应的编译错误提示。
5.2泛型集合
在集合创建时使用泛型(推荐使用),会在相机和中添加元素时对元素进行检查,如果类型不一致会出现编译错误的提示,在访问时(获取)会自动转型,避免强制转换的麻烦。
5.3泛型的语法
泛型分为类泛型和方法泛型
- 类泛型
- 在类定义时使用泛型(可以使用多个),类中的任意位置都可以使用该定义的泛型。
//T是一个可变化的类型,用户在使用时传入的是什么类型,就会变化什么类型
//如果用户没有传入,则就是Object类型
//在当前类中,只能当为Object使用,因为不确定用户会传入什么类型
//T也可以使用任意字母代替
//多个类型之间可以使用逗号隔开
public class MyClass<T,P> {
public T test(T object){
T t;
return null;
}
public T test1(T obj1,P obj2){
return null;
}
}
public class TestMain1 {
public static void main(String[] args) {
MyClass<String, Integer> c = new MyClass<>();
String test = c.test("2");
String s = c.test1("2", 4);
}
}
- 方法泛型
- 是指在类上面不定义泛型,在方法上定义泛型
public class MyClass1 {
//在方法上定义并使用泛型
public <T> T test(T obj){
T t;
return null;
}
//在方法中定义的泛型,在其他方法中不能使用
// public T test1(){
//
// }
}
public class TestMain1 {
public static void main(String[] args) {
MyClass<String, Integer> c = new MyClass<>();
String test = c.test("2");
String s = c.test1("2", 4);
MyClass1 c1 = new MyClass1();
//由于在方法中定义了泛型,且要求传入参数与返回值类型一致,所以也不需要强制转换,会自动检查类型
String str2 = c1.test("2");
}
}
经典面试题:
为什么说Java中的泛型是伪(假)泛型?
1、Java中的泛型仅对传入的参数进行编译检查,不会对真的对参数进处理。
2、Java中的泛型仅对对象使用时进行强制转换,不会真的对对象进行转型处理。
3、在对象真正使用的过程中,仍旧时Object类型。
5.4泛型中的super和extends
当作为参数时,传入的参数根据extends和super的不同,可以传入更宽泛的类型。
?extends B 表示可以传入B类或者B类的子类 ,称为上界限定符
?super B 表示可以传入B类或者B类的父类,称为下界限定符
在使用时,上界不存,下界不取
使用上界时,该集合不能存储数据,可以取值
使用下界时,集合不能取值,但是可以存储数据。
public class TestMain2 {
public static void main(String[] args) {
ArrayList<C> list = new ArrayList<>();
addAll(list);
}
//? extends B 表示B类或者B类的子类 上界限定符
//? super B 表示B类或者B类的父类 下界限定符
//相当于直接写B类,类型会宽泛一些。
//上界不存,下届不取
public static void addAll(ArrayList<? extends B> c){
//添加元素
// c.add(new B());
// for (B b:c){
// System.out.println(b);
// }
}
}
六、Collections工具类
Collections针对集合的帮助类,相当于Arrays是针对数组的帮助类。
主要是对集合的排序、打乱、截取等操作。
public class TestMain3 {
public static void main(String[] args) {
//sort
ArrayList<Integer> list = new ArrayList<>();
list.add(23);
list.add(35);
list.add(12);
list.add(7);
list.add(40);
list.add(14);
Collections.sort(list);//对集合进行排序
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
Collections.shuffle(list); //随机打乱一个集合
System.out.println(list);
//如果已知元素,可以使用asList方法将其转换成集合,但是不能强转成ArrayList,因为他是List接口
//一般用作循环使用。
List<Integer> list1 = Arrays.asList(23, 35, 12, 8, 10, 45, 13);
}
}
七、Set集合
无序,无下标,元素不能重复
7.1 HashSet
通过HashCode实现元素不可重复
当存入的元素hashCode相同时,会使用equals方法来判断是否相同元素,如果不相同,则可以存入。
注意:
HashSet判断不同对象的步骤是:
1、先判断hashCode是否相同,如果不同,直接认定是不同对象,如果相同,进行第二步。
2、判断equals是否相同,如果不同,才认定是不同对象,如果相同,认定为相同对象,会直接放弃添加。所以,在使用HashSet时,如果要存入实体类的对象,一定要对该实体类进行重写equals和hashCode方法。
注意: HashSet底层使用的HashMap,创建HashSet时会创建一个HashMap,每次添加元素时,就是向HashMap的key上面进行添加。
7.2 LinkedHashSet
LinkedHashSet继承自HashSet.
HashSet一般情况下使用HashMap作为底层,而LinkedHashSet使用的LinkedHashMap作为底层,在HashMap的基础上添加了链表的结构,来保存了添加的顺序。
public class TestMain2 {
public static void main(String[] args) {
LinkedHashSet<String> let = new LinkedHashSet<>();
let.add("b");
let.add("a");
let.add("c");
for (String s : let) {
System.out.println(s);
}
}
}
7.3 TreeSet
实现了Sorted接口,自动排序,根据自定的方式(基于Comparable接口)排序。
==注意:==使用TreeSet来存储元素时一定要指定排序的方式,否则会报错。
二叉树:
一个节点,最多只能有两个直接的子节点。
TreeSet进行排序的规则要求实现Comparable接口,如果是自定义的类,要将对象放入到TreeSet中,必须实现该接口,否则会出现ClassCastException。
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name){
super();
this.name = name;
}
public Student(String name,int age){
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
// if (this == o) return true;
// if (!(o instanceof Student)) return false;
// Student student = (Student) o;
// return name.equals(student.name);
return true;
}
@Override
public int hashCode() {
// return Objects.hash(name);
return 1;
}
@Override
public int compareTo(Student o) {
// if (this.age-o.age <0){
// return -1;
// }else if (this.age-o.age >0){
// return 1;
// }
return this.age-o.age ;
}
}
public class TestMain3 {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(20);
set.add(15);
set.add(30);
set.add(18);
set.add(12);
set.add(14);
set.add(10);
set.add(30);//与前面添加的30相等,放弃添加,保证元素不重复。
System.out.println(set);
//要使用TreeSet来添加自定义类的对象,需要实现比较接口。
TreeSet<Student> set1 = new TreeSet<>();
set1.add(new Student("张三",20));
set1.add(new Student("李四",18));
set1.add(new Student("王五",19));
set1.add(new Student("赵六",22));
System.out.println(set1);
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("张三",20));
list.add(new Student("李四",18));
list.add(new Student("王五",19));
list.add(new Student("赵六",22));
Collections.sort(list);
System.out.println(list);
}
}
上面用的Collections.sort方法进行排序时,如果是自定义对象,也需要实现Comparable接口
public class TestMain3 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("张三",20));
list.add(new Student("李四",18));
list.add(new Student("王五",19));
list.add(new Student("赵六",22));
Collections.sort(list);
System.out.println(list);
}
}
==注意:==Comparable接口中的comparableTo方法,返回负数会放到树的左边,证书放到树的右边,返回0,表示元素相等,会放弃添加。
八、Map体系
8.1 Map结构
Map是一个顶层接口,常用的实现类有HashMap、TreeMap等。
Map接口特点是:使用键值(key,value)对来存放数据。key键要求不能重复,value值可以重复。
//常用方法,
put(Object key,Object value);//添加
get(Object key);//获取元素
Set keySet();//返回所有的键集
Collection Values();//返回所有的值
注意: map存取元素时,需要使用key来访问,key可以是任意类型,但是推荐使用字符串。当使用相同的key存放时,会覆盖原来的内容。
8.2 HashMap基本使用
public class TestMain5 {
//HashMap的用法
public static void main(String[] args) {
//创建对象
HashMap<String,Student> map = new HashMap<>();
//添加元素
map.put("a",new Student("1","张三",20));
map.put("a",new Student("2","李四",21));
map.put("a",new Student("3","王五",22));
//获取元素
Student stu = map.get("b");
System.out.println(stu);
//遍历元素
//1、借助遍历key来遍历值
//得到keys键集
Set<String> set = map.keySet();
for (String key:set){
System.out.println("key为:"+key+",对应的值为"+map.get(key));
}
//2、直接获取值集合
Collection<Student> colls = map.values();
for (Student s:colls) {
System.out.println("值为:"+s);
}
//3、在遍历时获得键值
Set<Map.Entry<String, Student>> entrySet = map.entrySet();
for (Map.Entry<String, Student> entry:entrySet){
System.out.println("key为:"+entry.getKey()+",对应的值为:"+entry.getValue());
}
//删除元素
map.remove("a");
int size = map.size();//得到元素的个数
System.out.println(size);
}
}
经典面试题:
HashMap与HashTable的区别:(相同点:使用方法基本一样)
1、HashMap性能较高,但是线程不安全,HashTable线程安全,性能极低,基本不使用。
2、HashMap键和值都可以为null,但是键不能重复,会覆盖。HashTable键和值都不能为null。
8.3 HashMap的底层原理
既有比较快的查找性能,又有比较好的增删性能。
采用数组+链表+红黑树(JDK1.8之后)的结构。
1、创建HashMap时,指定了扩容因子为0.75
2、在第一次添加元素时,才会创建数组,大小为16,设置临界点为12
3、添加元素时,如果key相同,会覆盖值,会根据key的hash计算,并与数组长度求模,得到该元素存放的下标,如果该下标有元素,就直接放到链表最后一个元素,如果此时链表数量大于等于7,进行树化,变化为红黑树。
==注意:==红黑树的变化条件有两个:1、数组长度大于64,2、链表上的元素大于8
4、当元素添加到临界点时,再添加元素时,会进行扩容,扩容为原来的两倍,临界点重新设置为数组大小的*0.75
5、当扩容时,数组大小大于2^30,则指定int的最大值
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始大小 16
static final int MAXIMUM_CAPACITY = 1 << 30;//最大空间2^30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f;//扩容因子
static final int TREEIFY_THRESHOLD = 8;//变成树的临界值
static final int UNTREEIFY_THRESHOLD = 6;//变成链表的临界值
static final int MIN_TREEIFY_CAPACITY = 64;//变成树的最小容量,单条链表(桶)上的元素超过8个
//map中的链表结构(单向链表)
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;//哈希值
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
transient Node<K,V>[] table;//map中存放数据的数组,每个元素都是一个链表
//hash计算
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//无参构造
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
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),或者table中的元素数量为0
if ((tab = table) == null || (n = tab.length) == 0)
//设置n为16
n = (tab = resize()).length;
//hash值来模运算,如果原来的数组的首元素为空,直接赋值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//如果首元素不为空,则判断key的hash与equals是否相同
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//相同则覆盖
e = p;
//如果不同,并且为树形结构
else if (p instanceof TreeNode)
//向树形结构结构中添加元素
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//向链表中添加元素
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//如果当前桶上元素数量大于等于7,转换成红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//key相同则停止循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
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;
//如果size大于临界点
if (++size > threshold)
resize();//扩容
afterNodeInsertion(evict);
return null;
}
final Node<K,V>[] resize() {
//记住原来的数据
Node<K,V>[] oldTab = table;
//如果原来的数组为空,长度即为0,否则为原来的长度
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//记住原来的空间大小
int oldThr = threshold;
//新的大小暂时为0
int newCap, newThr = 0;
//如果原来的大小大于0
if (oldCap > 0) {
//如果原来的大小大于最大大小,直接赋值为最大值
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
//如果原来的大小扩容1倍后小于最大大小并且原来的大小等于16
//d大小扩容1倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
//临界点扩容1倍
newThr = oldThr << 1; // double threshold
}
//原来的临界点大于0
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;//新的空间等于老的临界点
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;//新的空间大小设置为16
//新的临界点为12
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//处理极限值
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;//将计算的临界值保存到属性中
@SuppressWarnings({"rawtypes","unchecked"})
//创建新的数组大小
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;//保存新的数组到属性中
//如果原来的数组元素不为空,则重新计算位置
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
8.4 Properties
Properties继承自HashTable,但是添加了setProperty和getProperty方法,只能操作字符串。
作用是用来加载系统的配置文件。所以里面还添加了load方法,专门用来加载配置。
public class TestMain7 {
public static void main(String[] args) {
Properties prop = new Properties();
//设置值
prop.setProperty("username","root");
prop.setProperty("password","root");
//获取值
System.out.println(prop.getProperty("username"));
}
}
8.5 TreeMap
TreeMap使用树的方式来存放数据,需要对key对应的类实现Comparable接口。当添加后,会自动排序。
public class Student implements Comparable<Student> {
private String id;
private String name;
private int age;
public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(id, student.id) && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
return this.id.hashCode() - o.id.hashCode();
}
}
public class TestMain8 {
//TreeMap的用法
public static void main(String[] args) {
TreeMap<Student,String> map = new TreeMap<>();
map.put(new Student("1","张三2",20),"aaa" );
map.put(new Student("2","李四1",22),"bbb" );
map.put(new Student("3","王五4",24),"ccc" );
System.out.println(map);
}
}