第八篇
Set集合:
特点:无序,不重复。
存储时采用了hash算法机制,计算存储位置。
HashCode方法:
Object是引用类型的父类,提供了hashCode()方法以及equals()方法,因此我们在定义类型时,一般都重写hashCode和equals方法。
重写的重要性:
equals方法我们用来判断集合中的元素是否重复,hashCode方法我们在使用Set集合时,必须要重写,因为,我们采用的hash算法计算Set集合元素的存储位置。
int hashCode():
Object提供的方法是通过地址计算hash值,不可控。
我们需要在自定义类中重写此方法。
重写原则:尽可能的让所有成员变量都参与运算尽可能的减少hash值的碰撞。
public int hashCode(){
int result = 7;--定义一个局部变量,进行初始化
int prime = 53 --定义一个局部变量,赋值为素数
result = prime*result+field1;
result = prime*result+field2;
return result;
}
Set集合的遍历:
因为Set集合是无序的,无下标可言,因此不能使用经典for循环。我们可以使用迭代器原理。
(1) 调用集合的iterator()获取迭代器
(2) 使用foreach循环
Set集合的元素:
不能轻易修改参与hash值算法的成员变量,否则容易引起内存溢出。
原因:成员变量修改后,会出现新的hash值,但是存储位置还在原hash值的位置上。因此操作时,找不到具体的存储位置。
子类:
HashSet:无序,不重复,底层使用hash算法计算存储位置。增加删除时效率高 。
LinkedHashSet:是HashSet的子类,底层使用hash算法计算存储位置,同时使用链表来维护顺序,顺序与添加顺序一致。在查看检索时,效率比较高。
TreeSet:是SortedSet子接口的实现类,使用二叉树的数据结构维护元素的顺序。
Map接口:集合框架中的另一个父接口
Map集合:用于储存一一对应的元素数据,第一个对象可以作为索引,第二个对象作为值,我们称之为key-value,键值对。
储存数据的特点:
(1)以key-value形式进行存储。
(2)key与value都必须是引用类型
(3)key可以为null。
(4)key与value是单向一对一映射。
(5)key不能重复
存储机制:
Map是基于数组和链表的数据结构进行存储数据。
作为key的对象采用了hash算法计算存储的数组 (散列数组,散列桶)的位置.如果计算出来的位置,数组中此位置没有元素,就可以添加到散列桶内,如果有元素,key的equals方法返回值为false,就会存储在散列桶元素对应的单向链表中。
如果key的equals方法返回true,就进行替换(覆盖)。
PS:使用Map集合,做为key的数据类型应该重写equals和HashCode方法
常用方法:
V put(K k,V v):作用:用于存储一对key-value. 返回被替换的value值,如果不是替换就返回null
V get(K k):作用:通过key对象,获取对应的value对象,如果集合中没有此key,返回null
Map集合的遍历
Set<K> keySet();
用于获取Map中所有的key对象,返回一个Set集合
Set<Entry<K,V>> entrySet();
将key-value封装成内部类对象,返回Entry对象的Set集合
Collection<V> values();
将map集合中的所有value封装到一个Collection集合中。
装载因子和HashMap的优化
装载因子:DEFAULT_LOAD_FACTOR = 0.75f
默认容量:DEFAULT_INITIAL_CAPACITY
16,就是数组的容量
元素个数: size
当我们创建一个HashMap对象时,底层数组的初始容量为16。当存储的数据的个数 size/DEFAULT_INITIAL_CAPACITY等于DEFAULT_LOAD_FACTOR时,数组开始扩容。此时最佳。
如果小于0.75扩容,比较占内存。
如果大于0.75扩容,操作的元素比较多。
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;
if ((tab = table) == null || (n = tab.length) == 0)
/* 没有存储元素,*/
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
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) {
/* hash碰撞 */
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
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;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
内部类Node,封装了一个key-value数据,同时还存储了下一个节点的引用 。
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;
}
Map接口的子类:
HashMap与HashTable的区别:
(1)HashTable是一个古老的类。不建议使用
(2)HashTable是一个线程安全的类,HashMap线程不安全
(3)HashTable的key不能是null,HashMap可以是null
LinkedHashMap:是HashMap子类,使用链表来维护key-value的顺序,在迭代时顺序与添加顺序一致。
TreeMap:
是SortedMap子接口的实现类,使用了二叉树的数据结构维护 填入集合的顺序。
(1)自然排序:
往TreeMap里添加的key对象,可以实现Comparable接口。重写 compareTo方法
(2)定制排序:做为key对象的数据类型,可以不实现Comparabel接口。
需要创建一个比较器Comparator对象,实现compare方法
Properties:
是HashTable的子类型,用于封装属性文件的key-value信息,因为在文件里写的都是字符串,因此Properties的key与value都是字符串类型
File类型
java.io.File类型,可以对硬盘上的文件以及目录,进行操作。
如查看文件/目录的属性信息,创建,删除文件/目录。此类型不能查看和修改文件里的内容。
常用构造器:
File(String pathname):
指定一个路径,创建一个File对象
路径:
(1)文件的路径,要写到文件的扩展名为止
(2)目录的路径,要写到当前目录的名称为止
常用方法:
boolean exists():判断指定的路径是否存在
boolean isFile();判断指定路径是不是文件
boolean isDirectory();判断指定路径是不是目录
String getName());获取文件/目录名称
long lastModified();获取文件/目录的最后修改时间
boolean isAbsolute();判断指定路径是不是绝对路径
String getAbsolutePath();获取绝对路径
String getParent();获取父目录的路径
long length();获取文件大小文件/目录创建方法:
boolean createNewFile();创建文件
boolean mkdir();创建目录
boolean mkdirs();创建多级目录
文件/目录的删除方法
boolean delete()
可以删除文件,删除目录时,需要目录下没有文件或子目录
File[] listFiles()
获取目录里的file对象
javaBean规范:程序开发者默认遵循的一种规范
(1)提供两个构造器
(2)给成员变量提供get/set方法
String name
getName()
setName(String name);
Bean:豆子的意思,get/set方法名上的后续单词称之为bean.
在命名方法时,作为bean的单词首字母要大写,成员变量要尽可能的与bean名一致,首字母小写。
(3)重写hashCode方法和equals方法
(4)重写toString()
递归:
递归思想:分成递与归。一层层递进,最后再一层层归。
两种递归:
(1) 方法调用自己
(2) 方法A调用方法B,方法B调用A
举例:
n*(n-1)*......*1
z = f(n) 计算n的阶乘
= n*f(n-1)
= n*(n-1)*f(n-2)
= n*(n-1)*......*1
f(n)是一个函数:
里的逻辑:
n*f(n-1)