别把人生,输给心情
师父说:心情不是人生的全部,却能左右人生的全部。
你有没有体会到,当你心情好的时候,生活仿佛阳光灿烂,顺风顺水,
当你心情不好的时候,似乎周围的一切都糟糕透了。
有时候,我们不是输给了别人,而是败给了坏心情的自己。
人活着就像一个陀螺,为了生活不停的转动,永远都有忙不完的事。
有时候又像沙漠中的骆驼,背负着重担努力地前行,却不知道哪里才是终点。
先现在情绪低落,只是因为陷进了自我纠缠的陷阱,等到熬过了这段苦难,
你会发现你所纠结的东西,真的只是无关痛痒的小事。
生活就像天气,不会总是晴天,也不会一直阴雨,喜欢和讨厌是次要的,关键是你要学会调整自己。
心静了,才能听见自己的心声,心清了,才能照见万物的本性。
假如任由坏情绪累积和蔓延,很多事只会变得越来越糟糕,
既然做不到让所有人都满意,为何不努力让自己开心?
生活是你自己的,喜怒悲欢都由你自己决定,记得别被坏情绪束缚住,
不要让你的人生,输给了心情。
—————— 一禅心灵庙语
@toc
添加、删除、修改操作:
V put(K key,V value); // 将指定的 key 和 value 值添加/修改到该集合当中。
void putAll(Map<? extends K,? extends V> m); // 将m中的所有key-value对存放到当前map集合当中
V remove(Object key); // 移除指定key的key-value对,并返回value
void clear(); // 清空当前map中的所有数据
int size(); // 返回此集合中存储的元素数据(键值对)的数量。
举例:
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
Map<String,Integer> map = new HashMap<String,Integer>();
// 添加元素数据:
map.put("zhangsan",66);
map.put("lisi",89);
map.put("wangwu",97);
map.put("lihua",99);
System.out.println(map);
int size = map.size(); // 返回该集合中存储的键值对的数量。
System.out.println(size);
System.out.println("*********************");
Integer zhangsan = map.remove("zhangsan"); // 移除key = zhangsan的元素数据,并返回该移除的value值。
System.out.println(zhangsan);
System.out.println(map);
map.clear(); // 清空该Map 集合当中的存储的元素数据
System.out.println(map.size());
}
}
举例
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
Map<String,Integer> map = new HashMap<String,Integer>();
// 添加元素数据:
map.put("zhangsan",66);
map.put("lisi",89);
Map<String,Integer> map2 = new HashMap<String,Integer>();
map2.put("wangwu",97);
map2.put("lihua",99);
map.putAll(map2); // 将 map2 集合中所有的key-value键值对添加到此 map集合当中去
// 注意:两者集合存储的元素数据类型必须是一致的才可以添加成功。
System.out.println(map);
}
}
元素查询的操作:
V get(Object key); // 获取指定key对应的value
boolean containsKey(Object key); // 判断该集合当中是否包含指定的 key 值。
boolean containsValue(Object value); // 判断判断该集合当中是否包含指定的 value 值。
boolean isEmpty(); // 判断此集合是否为 空,是返回 true,不是返回 false;
boolean equals(Object o); // 判断当前map和参数对象 o 是否相等
举例:
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
// Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
Map<String,Integer> map = new HashMap<String,Integer>();
// 添加元素数据:
map.put("zhangsan",66);
map.put("lisi",89);
map.put("wangwu",97);
map.put("lihua",99);
System.out.println(map.get("zhangsan")); // 获取到对应 key上的 value值
System.out.println(map.containsKey("zhangsan")); // 判断该集合当中是否存在 key = zhangsan的键值对
System.out.println(map.containsValue(99)); // 判断该集合当中是否存在 value = 99的键值对
System.out.println(map.isEmpty()); // 判断该集合是否为空
System.out.println(map.equals(map)); // 判断当前map和参数对象 o 是否相等
}
}
元视图操作的方法:
Set<K> keySet(); // 返回所有key构成的Set集合
Collection<V> values(); // 返回所有value构成的Collection集合
举例:
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
// Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
Map<String,Integer> map = new HashMap<String,Integer>();
// 添加元素数据:
map.put("zhangsan",66);
map.put("lisi",89);
map.put("wangwu",97);
map.put("lihua",99);
Set<String> keys = map.keySet(); // 返回此集合当中所有的 key 值存储到 Set 集合当中
for (String s : keys) {
System.out.println(s);
}
System.out.println("****************");
Collection<Integer> values = map.values(); // 返回此集合当中所有的 value 值存储到 Collection 集合当中
// Collection 接口集合可以使用迭代器
Iterator<Integer> iterator = values.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
Set<Map.Entry<K,V>> entrySet(); // 返回所有key-value对构成的Set集合
其中的 Map.Entry 表示的是一个接口,也可以理解为是一个类。
* Set<Map.Entry<K,V>> entrySet() 将 Map集合转换成 Set集合
* 假设现在有一个 Map集合 ,如下所示:
* map1 集合对象
* key value
* 1 zhangsan
* 2 lisi
* 3 wangwu
* 4 zhaoliu
*
* Set set = mop1.entrySet();
* set 集合对象
* 1=zhangsan
* 2=lisi
* 3=wangwu
* 4=zhaoliu
Map.Entry<K,V> 的图示:
举例:
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
// Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
Map<String,Integer> map = new HashMap<String,Integer>();
// 添加元素数据:
map.put("zhangsan",66);
map.put("lisi",89);
map.put("wangwu",97);
map.put("lihua",99);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
// getKey()获取 key 值,getValue()获取value值
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
Map.Entry
。判断两个 key 相等的标准是
: 两个key 通过 equals() 方法返回 true , hashCode 值也相等。判断两个 value 相等的标准
是: 两个 value 通过 equals() 方法返回 true。举例:
如下是 Set 中的 Key 存储自定义类 Person5 ,其中并没有重写Object 中的 equals() 方法和 hashCode()方法。会出现 Key 存储到重复的数据。
package blogs.blogs7;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapTest {
public static void main(String[] args) {
HashMap<Person5,Integer> hashMap = new HashMap<Person5, Integer>();
hashMap.put(new Person5("Tom",19),1);
hashMap.put(new Person5("Tom",19),1);
hashMap.put(new Person5("Tom",19),1);
hashMap.put(new Person5("zhangsan",23),4);
hashMap.put(new Person5("lihua",20),5);
// 遍历HashMap 集合
Set<Map.Entry<Person5,Integer>> entries = hashMap.entrySet();
for (Map.Entry<Person5,Integer> entry : entries) {
System.out.println(entry);
}
}
}
class Person5 {
String name;
int age;
public Person5() {
}
public Person5(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person5{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
修改: 重写其中的 Key 值的 Set 集合中存储的 类中的 equals() 和 hashCode() 方法。
package blogs.blogs7;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class HashMapTest {
public static void main(String[] args) {
HashMap<Person5,Integer> hashMap = new HashMap<Person5, Integer>();
hashMap.put(new Person5("Tom",19),1);
hashMap.put(new Person5("Tom",19),1);
hashMap.put(new Person5("Tom",19),1);
hashMap.put(new Person5("zhangsan",23),4);
hashMap.put(new Person5("lihua",20),5);
// 遍历HashMap 集合
Set<Map.Entry<Person5,Integer>> entries = hashMap.entrySet();
for (Map.Entry<Person5,Integer> entry : entries) {
System.out.println(entry);
}
}
}
class Person5 {
String name;
int age;
public Person5() {
}
public Person5(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person5)) return false;
Person5 person5 = (Person5) o;
return age == person5.age &&
Objects.equals(name, person5.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person5{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
HashMap中的 Key值可以存储添加 null 值,但是仅仅只能添加一个 null ,因为 Key 中的数据存储在 Set集合当中的,不可重复,而 Value 值也可以存储 null值,而且可以存储多个 null 值,因为 Value 值数据底层是存储在Collection集合当中的。
举例:
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class HashMapTest {
public static void main(String[] args) {
HashMap<String,Integer> hashMap = new HashMap<String, Integer>();
hashMap.put(null,null);
hashMap.put(null,null);
hashMap.put(null,null);
hashMap.put("1",null);
hashMap.put("2",null);
hashMap.put("3",null);
// 遍历HashMap 集合
Set<Map.Entry<String,Integer>> entries = hashMap.entrySet();
for (Map.Entry<String,Integer> entry : entries) {
System.out.println(entry);
}
}
}
常用方法总结:
JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
JDK 8版本发布以后:HashMap是数组+链表+红黑树实现
如下是 JDK8 的HashMap 结构图
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 HashMap的默认容量是 16
-----------------------------------------------------------------------------------
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30; // HashMap的最大支持容量,2^30
-----------------------------------------------------------------------------------
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f; // HashMap的默认加载因子
-----------------------------------------------------------------------------------
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8; // Bucket中链表长度大于该默认值,转化为红黑树
-----------------------------------------------------------------------------------
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6; // Bucket中红黑树存储的Node小于该默认值,转化为链表
-----------------------------------------------------------------------------------
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64; // 桶中的Node被树化时最小的hash表容量。(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
-----------------------------------------------------------------------------------
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table; // 存储元素的数组,总是2的n次幂
-----------------------------------------------------------------------------------
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/
transient Set<Map.Entry<K,V>> entrySet; // 存储具体元素的集合
-----------------------------------------------------------------------------------
/**
* The number of key-value mappings contained in this map.
*/
transient int size; // HashMap中实际存储的键值对的数量
-----------------------------------------------------------------------------------
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
transient int modCount; // HashMap扩容和结构改变的次数。
-----------------------------------------------------------------------------------
/**
* The next size value at which to resize (capacity * load factor).
*
* @serial
*/
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold; // 扩容的临界值,=容量 * 填充因子
-----------------------------------------------------------------------------------
/**
* The load factor for the hash table.
*
* @serial
*/
final float loadFactor; // 填充因子
数组 + 链表
的结合。当实例化一个 new HashMap() 时,实际上会创建一个长度为 Capacity 的 Entry 数组。这个长度在 哈希表中称为 容量(Capacity) ,在这个数组中可以存放元素的位置,我们称之为 ”桶“ (bucket) ,每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素。JDK8: HashMap 的内部存储结构其实是:数组+链表+树
的结合。当实例化一个 new HashMap 时,会初始化 initilCapacity 和 loadFactor ,在 put() 第一对映射关系(键值对)添加时,系统会创建一个长度为 initilCapacity 的 Node 数组 ,这个长度在哈希表中被称为 ”容量" (Capacity),在这个数组中可以存放元素的位置,我们称之为 “桶”(bucket) ,每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素。
每个bucket 中存储一个元素数据,既 一个 Node 对象,但每一个 Node 对象可以带一个引用变量 next ,用于指向下一个元素,因此,在一个桶中,就有可能生成一个 Node 链表。也可能是一个一个TreeNode 对象,每一个TreeNode 对象可以有两个叶子节点 left 和 right ,因此,在一个桶中,就有可能生成一个 TreeNode 树。而新添加的元素作为链表的 last ,或树的叶子节点。
JDK1.8 源码分析:
向 HashMap 集合中添加 put(key1,value1) 键值对, 首先调用 元素 key1 所在类的 hashCode() 方法,来得到该 key1对象的 hashCode(哈希) 值。
然后再根据得到的 hashCode (哈希)值,通过某种散列函数 计算除该对象在 HashSet 集合中底层Node[] 数组的存储位置(即为:索引下标位置),(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好)。
* 如果 **key1** 的hashCode() 哈希值 与 已经存在的数据的哈希值都 **相等**, 则调用 **key1** 元素所在类的 **equals()** 方法,, 判断比较所存储的内容是否和集合中存储的相等。
* 如果 **不相等** 也就是 **equals()** 方法,返回 **false** ,则此时 **key1-value1**添加成功。—— **情况3**
* 如果 **相等** 也就是 **equals()**方法,返回 **true**,不添加,替换掉其中存放的 **value** 值为 **value1** ,因为 **key1** 是唯一的不可重复的,但是其 对应的 **value** 值是可以重复的。对应上述 添加成功的
如下是查找图示:
假设将所有的hashCode()方法返回设定为不一样的值,可以吗?,有什么问题:
不行,因为这样的话,就导致 HashMap 集合底层的哈希表就成为了一维数组了,没有链表的概念了,更没有哈希表的概念了。
假设将所有的hashCode()方法返回设返回值固定为某个值,可以吗?,有什么问题:
答:不可以,设将所有的hashCode()方法,返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况下我们称为:散列分别不均匀。
什么时散列分布不均匀
假设我们有 100 个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的
扩容
当 put(Key1,value1) 添加键值对个数超过 数组大小(数组总大小 length ,不是数组中实际存放的键值对个数 size),时,就会进行数组扩容。loadFactor 的默认值:DEFAULT_LOAD_FACTOR)为0.75,这是一个折中的取值,也就是说,默认情况下,数组大小(DEFAULT_INITIAL_CAPACITY)为16 ,那么当 HashMap 中元素个数超过 16 * 0.75 = 12
(这个值就是代码中的 threshold值,也叫临界值)的时候,就把数组的大小扩展为 2 * 16 = 32
,即扩大 1倍 ,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作。所以在开发中如果我们可以预估计其存储的数据量,也就是 HashMap中存储元素的个数,那么就调用其HashMap(int num) 设定存储容量的大小,减少扩容次数,提高 HashMap的性能 。
树形化
当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。
补充:
关于映射关系的key是否可以修改 ???
answer:不要修改,映射关系存储到 HashMap 中会存储 key 的 哈希值 ,这样就不用每次查找时,重新计算每一个 Entry 或 Node (TreeNode)的 哈希值了,因此如果已经 put 到 Map 中的映射关系,再修改 key 的属性,而这个属性有参与 hashCode值的计算,那么会导致匹配不上。
为什么HashMap扩容时,不是数组满了的时候扩容而是达到一个的 0.75 的额度才扩容 ???
因为HashMap 集合的底层时由 链表 + 数组 + 树 构成的。由于链表的存在,HashMap 当中的数组不一定会存储满了。
以及涉及到 HashMap 集合性能最优的效果,散列均匀分布,所以是到达一定额度 0.75 是最好的情况了.
在这里插入图片描述
负载因子值的大小,对HashMap有什么影响 ???
/** * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f; // HashMap的默认加载因子
负载因子的大小决定了HashMap的数据密度。
负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,
造成查询或插入时的比较次数增多,性能会下降。
负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的
几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数。
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所有数据改为使用“红黑树”存储。当小于 8 时,有会变成链表的形式存储。
HashMap中的内部类:Node
LinkedHashMap中的内部类:Entry
举例:
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class LinkedHashMapTest {
public static void main(String[] args) {
// 创建 LinkedHashMap 集合对象
LinkedHashMap<String,Integer> linkedHashMap = new LinkedHashMap<String, Integer>();
// 添加元素数据(键值对)
linkedHashMap.put("lihua",99);
linkedHashMap.put("zhangsan",89);
linkedHashMap.put("lisi",79);
linkedHashMap.put("wangwu",69);
// 遍历 LinkedHashMap 集合
// 获取到key-value 存储的 Set Entry 内部类集合对象
Set<Map.Entry<String, Integer>> entries = linkedHashMap.entrySet();
// 获取到该 Set Entry 内部类集合的迭代器
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
java.lang.Comparable
的接口,而且所有 的 Key 应该是同一个类的对象(因为不是同一类型无法比较判断),否则将会抛出 ClasssCastException
自然排序,重写其中的 compareTo()
抽象方法) 。在Java当中所有的包装类和String都实现了该 java.lang.Comparable
接口。所以一般要实现该接口的都是自定的类。
public TreeMap(Comparator<? super K> comparator)v // 构造一个新的,空的树图,按照给定的比较器排序。
举例:
TreeMap 集合中存储自定义类 Person6 对象,其中的 Key 存储的类为自定义 Person6 对象,该对象重写了 equals() 和 hashCode()方法,但是没有重写比较器的情况,报异常:java.lang.ClassCastException
类型转换异常。
将其中的Person6 中 age 年龄,升序排列
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest {
public static void main(String[] args) {
// 创建TreeMap 集合对象,其中 Key 存储的类为自定义 Person6
TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>();
// 添加元素
treeMap.put(new Person6("lihua",18),99);
treeMap.put(new Person6("zhangsan",20),89);
treeMap.put(new Person6("lisi",25),79);
treeMap.put(new Person6("wangwu",19),69);
// 遍历集合
// 遍历 TreeMap 集合
// 获取到key-value 存储的 Set Entry 内部类集合对象
Set<Map.Entry<Person6, Integer>> entries = treeMap.entrySet();
// 获取到该 Set Entry 内部类集合的迭代器
Iterator<Map.Entry<Person6, Integer>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<Person6, Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
class Person6 {
String name;
int age;
public Person6() {
}
public Person6(String name, int age) {
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;
}
// 当对象中的 name 和 age 属性值相同返回 true,否则返回 fasle
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person6)) return false;
Person6 person6 = (Person6) o;
return getAge() == person6.getAge() &&
Objects.equals(getName(), person6.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
@Override
public String toString() {
return "Person6{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
修正: 对 Key 中所存储的类,提供比较器,方式一:自然排序,该存储类实现 java.lang.Comparable
接口,并重写其中的 CompareTo()重写方法。
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest {
public static void main(String[] args) {
// 创建TreeMap 集合对象,其中 Key 存储的类为自定义 Person6
TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>();
// 添加元素
treeMap.put(new Person6("lihua",18),99);
treeMap.put(new Person6("zhangsan",20),89);
treeMap.put(new Person6("lisi",25),79);
treeMap.put(new Person6("wangwu",19),69);
// 遍历集合
// 遍历 TreeMap 集合
// 获取到key-value 存储的 Set Entry 内部类集合对象
Set<Map.Entry<Person6, Integer>> entries = treeMap.entrySet();
// 获取到该 Set Entry 内部类集合的迭代器
Iterator<Map.Entry<Person6, Integer>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<Person6, Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
class Person6 implements Comparable<Person6>{
String name;
int age;
public Person6() {
}
public Person6(String name, int age) {
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;
}
// 当对象中的 name 和 age 属性值相同返回 true,否则返回 fasle
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person6)) return false;
Person6 person6 = (Person6) o;
return getAge() == person6.getAge() &&
Objects.equals(getName(), person6.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
/**
* 升序的比较规则:
* this > 参数 ,返回 > 0
* this < 参数,返回 < 0
* this == 参数,返回 == 0;
* 降序反过来:
* this > 参数 ,返回 < 0
* this < 参数,返回 > 0
* this == 参数,返回 == 0;
*/
@Override
public int compareTo(Person6 o) {
// 首先判断该需要比较的参数是否是同一个实例,同一个实例的对象才能比较
if(o instanceof Person6) { // 其实这里我们使用了<Person3 o> 泛型限定了,就不需要判断了
Person6 person6 = (Person6) o; // 是对应的实例向下转型。
if(this.age > person6.age) {
return 1;
} else if( this.age < person6.age) {
return -1;
} else {
return 0;
}
} else {
// throw 可以替代 return
throw new RuntimeException("类型不一致"); // 抛出运行时异常
}
}
@Override
public String toString() {
return "Person6{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
修改方式二:定制排序 创建 TreeMap 集合对象时,将一个匿名实现Com 的类,作为参数,传递给构造器。该匿名实现类定制排序按照你 Perso6 中的 age 年龄降序排列
public TreeMap(Comparator<? super K> comparator); // 构造一个新的,空的树图,按照给定的比较器排序。
package blogs.blogs7;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest {
public static void main(String[] args) {
// 创建一个 TreeMap 集合对象,将匿名实现的比较器(定制排序),作为参数,传递给构造器
TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>(new Comparator<Person6>() {
@Override
public int compare(Person6 o1, Person6 o2) {
// 判断是否是对应比较的实例,其实这里我们可以不用判断的,因为使用的泛型限定
if(o1 instanceof Person6 && o2 instanceof Person6) {
Person6 p1 = (Person6)o1;
Person6 p2 = (Person6)o2; // 向下转型为对应的实例对象,从而获取比较属性
if(p1.age > p2.age) {
return -1;
} else if(p1.age < p2.age) {
return 1;
} else {
return 0;
}
}
// throw 可以代替 return
throw new RuntimeException("类型不一致"); // 抛出运行时异常
}
});
// 添加元素
treeMap.put(new Person6("lihua",18),99);
treeMap.put(new Person6("zhangsan",20),89);
treeMap.put(new Person6("lisi",25),79);
treeMap.put(new Person6("wangwu",19),69);
// 遍历集合
// 遍历 TreeMap 集合
// 获取到key-value 存储的 Set Entry 内部类集合对象
Set<Map.Entry<Person6, Integer>> entries = treeMap.entrySet();
// 获取到该 Set Entry 内部类集合的迭代器
Iterator<Map.Entry<Person6, Integer>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<Person6, Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
class Person6 implements Comparable<Person6>{
String name;
int age;
public Person6() {
}
public Person6(String name, int age) {
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;
}
// 当对象中的 name 和 age 属性值相同返回 true,否则返回 fasle
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person6)) return false;
Person6 person6 = (Person6) o;
return getAge() == person6.getAge() &&
Objects.equals(getName(), person6.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
@Override
public String toString() {
return "Person6{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
synchronized
。java.lang.NullPointerException
空指针异常。原容量 * 2 + 1
;HashMap和HashTable的比较:
举例 :
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashtableTest {
public static void main(String[] args) {
Hashtable<String,Integer> hashtable = new Hashtable<String,Integer>();
hashtable.put("lihua",1);
hashtable.put("zhangsan",2);
hashtable.put("lisi",3);
hashtable.put("wangwu",4);
Set<Map.Entry<String, Integer>> entries = hashtable.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
public Object setProperty(String key,String value); // 致电Hashtable方法put 。 提供与getProperty方法的并行性 。 强制使用字符串的属性键和值。 返回的值是Hashtable调用put的结果。简单的说:就是向Property 集合中添加键值对元素。
public String getProperty(String key); // 通过 key 找到对应的 value值,如果没有找到返回 null
举例:
首先我们先在项目中(注意添加到顶级项目中也就是如下的Test 项目下,不是Test2,或者 day模块下 ,不然无法读取到)添加一个属性文件(以.properties
后缀的配置文件)用于Properties 集合读取,内容如下:
name=Tom
password=abc123
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) {
// Properties 集合常用来处理配置文件:key 和 value 都是String类型
Properties properties = new Properties();
try {
// IO流读取文件信息,需要异常处理
FileInputStream fileInputStream = new FileInputStream("jdbc.properties"); // 文件名
properties.load(fileInputStream); // 加载流对应的文件,同样需要异常处理
} catch (IOException e) {
throw new RuntimeException(e);
}
String name = properties.getProperty("name");
String password = properties.getProperty("password"); // 根据对应文件中的 key 值获取到对应的value值
System.out.println(name);
System.out.println(password);
}
}
Map 接口下的集合的遍历方式:注意:Map 集合中没有下标可以访问的。也没有迭代器可以使用的。
方式一: 普遍使用,二次取值。通过获取 keySet()
方法获取到 Map 集合中所有的 key 值,返回一个 Set 集合。再通过遍历 Set 集合中存储的所有的 key ,使用 get(key)
方法获取到对应的 value值。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Traverse {
// Map 集合遍历方式一:
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
// 1.获取到该 Map 集合当中的所有 Key 值
Set<String> keys = map.keySet();
// 2.遍历所有的 key 值
for (String key : keys) {
// 3. 通过 key 获取到对应的 value 值
System.out.println(key + "--->" + map.get(key));
}
}
}
方式二: 通过使用 entrySet()
方法,返回一个:Set< Map.Entry<K, V> >
集合对象, 再通过获取到该 Set<Map.Entry> 集合 的迭代器,通过迭代器遍历,获取到Map.Entry中存储的 key(getKey()), value(getVale()) 方法
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Traverse {
// Map 集合遍历方式二:
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
// 1. 获取到 Set<Map.Entry> 集合对象
Set<Map.Entry<String, String>> entries = map.entrySet();
// 2. 获取到该 Set<Map.Entry> 集合 的迭代器
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
// 3. 通过迭代器遍历,获取到Map.Entry中存储的 key,value值
while(iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
方式三: 推荐,尤其是容量大时。因为这是一次性获取到 Map 中所有的key-value 值后,再取出的,效率高
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Traverse {
// Map 集合遍历方式三:
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
}
方式四: 通过Map.values() 返回一个Collection 集合遍历所有的value,但不能遍历key
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Traverse {
// Map集合遍历方式四:
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
// 获取到集合当中所有的 value值
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
}
}
一个独立的集合 工具类
s
多了个 s 的是 集合工具类,都是在 java.util.
包下的。排序:
public static void reverse(List<?> list); // 反转 List 中元素的顺序
public static void shuffle(List<?> list); // 对 List 集合元素进行随机排序
java.lang.Comparable
的接口 / 定制排序 Comparator接口)public static <T extends Comparable<? super T>> void sort(List<T> list); // 根据元素的自然顺序对指定 List 集合元素按升序排序
public static <T> void sort(List<T> list,Comparator<? super T> c); // 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
public static void swap(List<?> list,int i,int j); // 将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找,替换 :
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll); // 根据元素的自然顺序,返回给定集合中的最大元素
public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp); // 根据 Comparator 指定的顺序,返回给定集合中的最大元素
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll); // 根据元素的自然顺序,返回给定集合中的最小元素。
public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp); // 根据Comparator 指定的顺序,返回给定集合中的最小元素。
public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal); // 用新值替换List 对象的所有旧值.
public static int frequency(Collection<?> c,Object o); // 返回指定集合中指定元素的出现次数
public static <T> void copy(List<? super T> dest,List<? extends T> src); // 将 src 集合中的内容复制到 dest 集合当中
注意该 copy(List dest,List src) 方法,两个集合对象存储的数据类型是必须是一样的,不然无法拷贝添加到 dest 集合当中的。
还有拷贝存储到的对象 dest 的 size()长度 < 被拷贝的 src 的 size()长度 就会拷贝失败,报异常: IndexOutOfBoundsException
所以拷贝存储到的对象 dest 的 size()长度 必须 >=
被拷贝的 src 的 size()长度 。注意是 size()实际存储元素数据的长度,不是length()集合的长度 。
如下源码:
举例 : 解决思路如下:
package blogs.blogs7;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsTest {
public static void main(String[] args) {
List<Integer> src = new ArrayList<Integer>();
src.add(1);
src.add(99);
src.add(-1);
src.add(66);
// 创建一个 和 src 集合存储类型一样的 Arrays.asList(new Integer[src.size()])数组,并设置该数组的大小长度为 src.size()
// 再使用这个数组创建拷贝存储的 desc 集合对象,默认数组没有添加数据(这里的是 null)
// 这样就 desc 就拥有了一个和 sec集合一样大小的 size()长度了。
List<Integer> desc = new ArrayList<Integer>();
desc = Arrays.asList(new Integer[src.size()]);
for (Integer integer : desc) {
System.out.println(integer);
}
// copy()拷贝
Collections.copy(desc, src);
System.out.println("*********** 拷贝 ************");
for (Integer num : desc) {
System.out.println(num);
}
}
}
补充 :各个集合的转换,可以使用对应的方法,或构造器
@Test
public void test2() {
Set<String> set = new HashSet<>();
set.add("king");
set.add("kingsoft");
set.add("king2");
set.add("king1");
// 将Set集合转换成List集合
List<String> myList = new ArrayList<>(set);
for(String s : myList) {
System.out.println(s);
}
}
equals() 和 hashCode()
方法。不然无法处理 Key 的不可重复特点 。。但是其中的 value 值是可以存储重复的数据的。而 value 值则是被 Collection 接口集合存储的。Map.Entry
。所有的 Map.Entry 构成的集合是 Set 无序的,不可重复的。java.lang.Comparable
的接口,而且所有 的 Key 应该是同一个类的对象(因为不是同一类型无法比较判断),否则将会抛出 ClasssCastException
自然排序,重写其中的 compareTo()
抽象方法) 。在Java当中所有的包装类和String都实现了该 java.lang.Comparable
接口。所以一般要实现该接口的都是自定的类。限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!!!
在这里插入图片描述
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。