Java集合初体验(谨个人学习记录)
概述
Java 集合可分为 Collection 和 Map 两种体系
- Collection 接口 :单列数据 。定义了存取一组对象的方法的集合
- List :有序列表 ,可重复的集合
- Set :无序列表 ,不可重复的集合
- Map 接口 :双列数据 。保存具有映射关系 “key” :“value” 键值对 的集合
Collection 接口继承树

Map 接口继承树
{name:"张三"} >> 键值对

Collection 接口方法
| 接口方法 | 作用 |
|---|---|
| add(Object obj) addAll(Collection coll) |
添加元素 |
| int size() | 获取有效元素的个数 |
| void clear() | 情况集合 |
| boolean isEmpty() | 是否是空集合 |
| boolean contains(Object obj) | 是否包含某个元素 ,通过 equals() 判断是否为同一个对象 |
| boolean containsAll(Collection coll) | 是否包含某个元素 ,拿两个集合的元素挨个比较 |
| boolean remove(object obj) | 删除元素 ,删除第一个匹配的元素 |
| boolean retainAll(Collection c) | 取两个集合的交集 |
| Object[] toArray() | 转成对象数组 |
| iterator() | 迭代器对象 ,集合遍历 |
Collection 子接口之一 :List接口 概述
- 使用 List 替代 数组 。
- 元素有序 且 元素可以重复
- 每个元素对应一个索引值
- 常用实现类 :ArrayList 、LinkedList 、Vector [线程安全]
ArrayList 源码分析 【底层使用 数组存储 】查询快 ,使用索引值查 [线程不安全 ]
jdk 1.7 :
构造器初始化的时候.就创建一个 长度为 10 的数组容量 Object[] elemrntData;
jdk 1.8 :
构造器初始化的时候并不会创建数组长度 ,而是在第一次 调用 list.add(); 方法的时候,才去创建数组长度为 10
后续操作与 jdk 1.7 无异
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;
}
}
List 接口方法
| 接口方法 | 作用 |
|---|---|
| void add(int index, Object ele); | 在 index 索引位置插入 元素 ele |
| boolean addAll(int index, Collection eles); | 从 index 索引位置开始将集合 eles 中的所有元素添加进来 |
| Object get(int index); | 获取指定 index 索引值位置的元素 |
| int indexOf(Object obj); | 返回元素 obj 在集合中首次出现的位置 |
| int lastIndexOf(Object obj); | 返回元素 obj在当前集合中最后一次出现的的位置 |
| Object remove(int index); | 移除指定 index索引值位置的元素 ,并返回此元素 |
| Object set(int index, Object ele); | 设置指定 index索引值位置的元素为 ele |
| List subList(int fromIndex, int toIndex); | 返回从 fromIndex 到 toIndex 索引值位置的子集合 。 |
**总结 :List 常用方法 **
增 :add(Object obj); / add(int index, Object obj);
删 :remove(int index); / remove(Object obj);
改 :set(int index, Object ele);
查 :get(int index);
长度 :size();
遍历 :
① 、迭代器 Iterator
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next);
}
②、forEach
for (Object obj : list) {
System.out.println(obj);
}
③、for 循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
Collection 子接口之二 :Set 接口
简介 :
- Set 接口是Collection的子接口 ,Set 接口没有提供额外的方法
- Set 集合不允许包含相同的元素 ,否则会添加操作失败
- Set b判断两个对象是否相同 根据 equals() 方法 。而不是根据 “==” 运算符 。
常用实现类之一 :HashSet
HashSet 底层 :数组 + 链表 的结构
-
HashSet 按照 Hash 算法来存储集合中的元素 ,具有很好的 存取 、查找 、删除 性能 。
-
特点 :
- 无序列表
无序性 :不等于随机性 。
是指 存储的数据在底层数组中并非按照数组索引的顺序添加 ,而是根据数据的哈希值确定存储位置
- 不是线程安全
- 元素可以为
null
-
HashSet 集合判断两个个元素相等的标准
- 两个对象通过 hashCode() 方法比较相等 。并且两个对象的 equals() 方法返回值相等 。
-
存放在 Set 容器中的对象 ,必须重写 equals() 方法和 hashCode(Object obj); 方法
-
不可重复性 :
是指 保证添加的元素按照 equals() 判断时 ,不能返回 true . 即:相同的元素只能添加一个 。
-
添加元素的过程 :以HashSet 为例 :
向HashSet 中添加元素a ,首先调用元素a所在类的hashCode() 方法 ,计算元素a的哈希值 ,
此哈希值接着通过某个算法计算出在 HashSet 底层数组中的存放位置 (索引位置),判断
数组对应索引位置是否已经有元素 :
情况一 :
如果此位置上没有其他元素 ,则元素a添加成功 。
情况二 :
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值 :
如果hash值不相同 ,则元素a添加成功 。
情况三 :
如果hash值相同 ,进而需要调用元素a所在类的equals() 方法 :
equals() 返回 true ,元素 a 添加失败 。
equals() 返回 false ,则元素 a 添加成功 。
Set 实现类之三 :LinkedHashSet
- LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置 。但它同时使用双向链表维护元素的次序 。
- LinkedHashSet 插入性能略低 HashSet ,但在 迭代器访问 Set 里的全部元素时有很好的性能 。
- 不允许集合元素重复 。
Set 实现类之三 :TreeSet
- TreeSet 是 SortedSet 接口的实现类 。TreeSet 可以确保元素处于排序状态 。
- TreeSet 底层使用
红黑树数据结构存储数据 。 - TreeSet 两种排序方法 :自然排序 和 定制排序 。默认情况下 TreeSet 采用自然排序
instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例
1、向 TreeSet 中添加数据 ,要求必须是相同类的对象 ,例如 :要么就都是整型 ,要么都是字符串 ,不能参半
2、两种排序方式 :自然排序(实现 Comparable 接口) 和 定制排序(Comparator)
3、自然排序中 ,比较两个对象是否相同的标准 :comparmTo() 返回0
1、自然排序
public class User implements Comparable {
private Integer age;
// ... 省略set get
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
return this.age.compareTo(user.age);
} else {
throw new RuntimeException("类型不匹配 ");
}
}
/*
Set seen = new TreeSet();
seen.add(new User(12));
seen.add(new User(123));
seen.add(new User(456));
*/
2、定制排序
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User) {
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(), u2.getAge()); // 从小到大
// return Integer.compare(u1.getAge(), u2.getAge()); 从大到小
} else {
throw new RuntimeException("类型不匹配 ");
}
}
};
Set seen = new TreeSet(comparator);
seen.add(new User(12));
seen.add(new User(123));
seen.add(new User(456));
Map :接口
简介 :
HashMap [主要实现类],线程不安全,可以存储nul键值对
- key :无序的,不可用重复 ,使用 Set 存储所有的 keu ,存储类对象 需要 重写类的 hashCode 和 equals
- value :无序的,可重复的 ,使用 Collection 存储 value 。存储类对象需要重写 equals
- 一个键值对key:value 构成一个 Entry 对象 entry无序的,不可重复读, 使用 Set 存储entry 。
LinkedHashMap [对于频繁的遍历] ,效率高于HashMap
TreeMap [排序 使用 key作为排序条件]
Hashtable [线程安全,效率低] jdk1.0 ,目前不推荐使用 。
面试题
HashMap 的底层 :在jdk7及以前 是 数组 + 链表 结构
在 jdk1.8 是 数组+链表+红黑树 结构
HashMap的底层实现原理 ?以 jdk 1.7 为例
HashMap map = new HashMap();
在实例化以后 ,底层创建了长度为 16 的一维数组 Entry[] table.
map.put(key1,value1);
首先,调用 key1 所在类的hashCode() 计算 key1 的哈希值 ,此哈希值经过算法计算后,得到在Entry数组中的存放位置 。
如果此位置上的数据为空 ,此时的 key1-value1 添加成功 --- 情况 1
如果此位置上的数据不为空 ,(此位置已存在数据 ,),比较 key1 和已经存在的数据的哈希值 : 如果 key1 的哈希值与已经存在的数据的哈希值不相同 ,此时 key1-value1 添加成功 --- 情况 2
如果 key1 的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同 ,则继续比较 :调用 key1 所在类的equals(key2)方法 ,比较 : 如果equals() 返回 false ,此时 key1-value1 添加成功 --- 情况 3
如果 equals() 返回true ,使用 value1 替换 value2 .
扩容问题 :扩容为 原来的两倍 。
补充 :关于情况2和情况3 :此时 key1-value1 和原来的数据以链表的形式存储
| 接口方法 | 作用 |
|---|---|
| 添加、删除、修改操作 | |
| Object put(Object key, Object value) | 将指定key-value添加(修改)到当前map对象中 |
| void putAll(Map m) | 将 Map m 中的所有key-value键值对存放到当前map中 |
| Object remove(Object key) | 移除指定 key 的 key-value键值对 ,并返回 value |
| void clear() | 清空当前 map 中的所有数据 |
| 元素查询操作 | |
| Object get(Object key) | 获取指定 key 对应的 value |
| boolean containsKey(Object key) | 是否包含指定的 key |
| boolean containsValue(Object value) | 是否包含指定的 value |
| int size() | 返回 map 中 key-value 键值对的个数 |
| boolean isEmpty() | 判断当前 map 是否为空 |
| boolead equals(Object obj) | 判断当前 map 和参数对 obj 是否相等 |
| 元视图操作 | |
| Set keySet() | 返回所有 key 构成的 Set 集合 |
| Collection values() | 返回所有的 value 构成的 Collection 集合 |
| Set entrySet() | 返回 所有 key-value 键值对 构成的 Set 集合 |
面试题
谈谈你对 HashMap 中 put/get方法的认识 ?
再谈谈 HashMap 的扩容机制 ?默认大小是多少?什么是负载因子(或填充比)
什么是吞吐临界值 (或阀值 、threshold)
HashMap 源码中的常量

Map 源码分析 :jdk 1.8 (未完)
简要说明
jdk 1.8 初始化容量 是在 第一次 put 的时候 对数组进行初始化 。
jdk 1.7 是在构造器初始化容量大小 .
1.8 HashMap 结构图

- 一 :JDK 1.8 中 涉及的 数据结构
1、位桶数组 (定义存储数据的数组) 【数组 】
transient Node<K,V>[] table; // 键值对 <k,v>2、数组元素 Node<k,v> ,实现类 Entry 接口 【链表 】
Node 是单向链表,Node<k,v>是一个内部类 ,相当于是 一个 节点类
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; // 构造器 Hash值 ,键 ,值 ,下一个节点 (当前节点的下一个节点引用 。) 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; } // 判断两个 Node 是否相等 ,若 key 和 value 都相等 ,返回true 。可以与自身比较为true / 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; } }3、红黑树 【树结构】
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // 父节点 TreeNode<K,V> left; // 左子树 TreeNode<K,V> right; // 右子树 TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; // 树节点颜色属性 TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } /** * Returns root of tree containing this node. */ // 返回当前节点的 根节点 。 final TreeNode<K,V> root() { for (TreeNode<K,V> r = this, p;;) { if ((p = r.parent) == null) return r; r = p; } }
- 二 : 构造器
// 1、初始化时 指定初始容量和负载因子 .如果指定的(初始容量为负数或者负载因子非正).抛出异常 IllegalArgumentException public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } // 2、指定初始容量 使用默认的负载因子 0.75f public HashMap(int initialCapacity) { // 调用的还是 两个参数的构造器 this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 3、没有参数 ,默认初始容量 16 ,默认负载因子 0.75f public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } // 4、用 m 的元素初始化散列映射 public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); }
- 三 : HashMap 的存取机制
1、get(Object key); 如何 getValue 值 【获取指定 key 对应的 value】
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; // Entry 对象数组 Node<K,V> first, e; // 在 tab 数组中经过散列的第一个位置 int n; K k; // 找到插入的第一个 Node节点 ,方法是 hash值和 n-1 相与 ,tab[(n - 1) & hash]) // 在一条链上的hash值是相同的 if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { // 检查第一个 Node节点 是不是要找的 Node if (first.hash == hash && // always check first node // 判断条件是 hash值要相同 ,并且 key值相同 ((k = first.key) == key || (key != null && key.equals(k)))) return first; // 检查 first 后面的 Node if ((e = first.next) != null) { if (first instanceof TreeNode) // 如果是树节点的一个实例 ,则调用树的方法 查找目标节点 return ((TreeNode<K,V>)first).getTreeNode(hash, key); /*遍历链表.找到 key值和 hash值 都相等的 Node*/ do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } // 没有返回 null return null; }get(key) 方法时获取 key 的 hash值 ,计算 hash&(n-1) 得到在链表数组中的存储位置 [ first=tab[hash&(n-1)] ] ,
先判断 first 的 key 是否等于参数 key ,不等就遍历后面的链表找到相同的 key值 返回对应的 value值即可 .
**2、put(key, value); ** 【将指定key-value添加(修改)到当前map对象中】
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; // 位桶初始化 默认容量 16 // 如果 tab在((n-1)&hash)的值是空 ,就新建一个节点插入到该位置 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); /*表示有冲突,开始处理冲突*/ else { Node<K,V> e; K k; // 检查第一个Node, P是不是要找的值 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) // 如果p 已经是树节点的一个实例 ,即这里已经是树结构了 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // p与新节点既不完全相同 ,也不是 TreeNode 实例 .普通节点 for (int binCount = 0; ; ++binCount) { /*指针为空,就挂在后面 */ if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // 指向 新节点 ,将新节点插入到链表尾部 /*如果冲突的个数已经达到8个 ,判断是否需要改变冲突的存储结构 treeifyBin 首先判断当前的 HashMap 长度 ,如果个数不足64,只进行扩容 resize(),扩容tab,如果个数达到64,将冲突的存储结构变为红黑树 . */ 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; // 相当于 pNode = pNode.next ,循环变量 } } /*如果链表上有相同的 key值 新的value 替换(覆盖) 旧的value*/ if (e != null) { // existing mapping for key 键的现有映射 ,键的 value 存在 V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); // 用于 LinkedHashMap return oldValue; // 返回存在的 value值 } } ++modCount; /*如果当前大小大于门限 ,门限是初始容量的0.75倍*/ if (++size > threshold) resize(); // 扩容两倍 afterNodeInsertion(evict); // 用于 LinkedHashMap return null; }1、判断键值对数组 tab[] 是否为空或为null ,否则以默认大小 resize() ;
if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length;2、根据键值 key 计算hash值 得到插入的数组索引 i ,如果 tab[i] == null ,直接新建节点添加 . 不为null 转入第三步
if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);3、判断当前数组中处理 hash 冲突的方式是链表还是红黑树(check 第一个节点类型即可),分别处理 .
- 四 :resize() 扩容机制
构造 hash 表时,如果不指定初始大小 ,默认容量大小是 16 (Node 数组大小16) ,如果Node[] 数组中的元素达到(填充比 * Node.length) , 则重新调整 HashMap 大小 变为原来的两倍大小 ,【扩容很耗时!】
final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; // 第一次put oldCap=0 int oldThr = threshold; // 当前临界值 threshold=0 赋值给 oldThr int newCap, newThr = 0; // 如果旧表的长度不为空 if (oldCap > 0) { // 第一次put 不进去 if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } /*把新表的长度设置为旧表长度的两倍 ,newCap=2*oldCap */ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) /*把新表的门限设置为旧表门限的两倍 ,newThr = 2*oldThr */ newThr = oldThr << 1; // double threshold } /*如果旧表的的长度是0,说明是第一次初始化表*/ else if (oldThr > 0) // initial capacity was placed in threshold // 第一次put 不进去 newCap = oldThr; else { // zero initial threshold signifies using defaults // 第一次put 进去 newCap = DEFAULT_INITIAL_CAPACITY; // 将默认容量16 赋值给 newCap newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);// newThr=(默认容量*加载因子)=12 } 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]; // new Node ,数组出现容量是16 newCap=16 , table = newTab; // 把新表赋值给 table /*如果旧表不为空 ,把旧表中的数据移动到新表中 */ 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) // 说明旧表这个Node 没有链表 ,直接存放在新表的 e.hash & (newCap - 1)位置 . newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) // e 是 树结构的实例 ,则进行红黑树的重hash分布 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); /*如果e后面有链表,表示e后面链着单链表 ,需要遍历链表 .将每个节点重新计算在新表的位置,并进行搬运 .*/ 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; // 记录下一个节点 /*新表是旧表的两倍容量 ,实际上把链表分为两队 , e.hash & oldCap偶数一对 , e.hash & oldCap奇数一对 */ 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) { //lo队不为null ,放在新表原位置 loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { //hi队不为null ,放在新表 j + oldCap 位置 hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; // 返回新表 }
- 五 :JDK1.8 使用红黑树的改进
在 jdk 1.7 中 ,HashMap 结构为 数组+链表 。
在 jdk 1.8 中 ,HashMap 处理 “碰撞” 情况增加了 红黑树这种数据结构 ,当碰撞节点较少,采用链表存储 ,当 阀值 > 8 时 ,
并且 数组个数达到 64 ,则采用红黑树 (特点 : 查询时间是 O(logn)),存储 ,将链表存储转换成红黑树存储 。
最坏的情况即 所有的key 都映射到同一个 位桶(数组索引位置)中 ,HashMap就退化成一个链表 ,查询时间从O(1) 到 O(n)
升级为 红黑树(也是一个二叉树)
使用 hash值 作为树的分支变量 ,如果两个 hash 值不相等 ,但指向同一个数组索引 ,hash 值 较大的会插入右子树 。
如果 hash 值相等 ,key 值 最好实现了Comparable ,可以按顺序插入 (自然排序 、定制排序 ) .
遍历方式 :
// 第一种 此方式可以同时取出 key value 的值
Iterator<Map.Entry<String,Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> next = iterator.next();
System.out.println(next.getKey() + next.getValue());
}
// 第二种 先取出 key 的值 ,然后通过get(key)方法 获取value 效率较低
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next + map.get(next));
}
LinkedHashMap (了解)
简介 :
LinkedHashMap 是 HashMap 的子类 ,内部也是调用父类的 put 方法 ,只是 创建Node 节点的时候 ,调用 LinkedHashMap 本身的 newNode()方法
// LinkedHashMap 部分源码
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
TreeMap (了解)
简介 :
向 TreeMap 中添加 key-value ,要求 key 必须由同一个类创建的对象 。
因为要按照 key 进行排序 :自然排序 、定制排序 。(也只能说 key 排序 ,value排序无效 )
Properties 处理属性文件
Properties 是 Hashtable 的子类
Properties pros = new Properties();
FileInputStream fs = new FileInputStream("类路径下的 properties后缀文件 ");
pros.load(fs); // 加载流对应的文件
String name = pros.getProperties("key");
String age = pros.getProperties("value");

浙公网安备 33010602011771号