8.4 String类的哈希值
字符串类重写方法hashCode(),自定义了哈希值,哈希值的计算方法是:
h = 31 * 上一次的计算结果 + 字符数组中元素的ASCII码值
*31 的目的,减少相同哈希值的计算
String类的哈希值:
//字符串String对象的哈希值 private static void stringHash(){ String s1 ="abc"; String s2 ="abc"; System.out.println(s1 == s2); //T //String类继承Object,可以使用方法hashCode System.out.println(s1.hashCode() == s2.hashCode()); //T /** * String类继承Object类 * String类重写父类的方法 hashCode() 自己定义了哈希值 */ System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); System.out.println("============="); /** * 字符串内容不一样,有没有可能计算出相同的哈希值 * String s1 ="abc"; * String s2 ="abc"; */ String s3 = "通话"; String s4 = "重地"; //1179395 //1179395 System.out.println(s3.hashCode()); System.out.println(s4.hashCode()); System.out.println(s3.equals(s4)); }
8.5 哈希值的相关问题
问题 : ① 两个对象A、B 两个对象哈希值相同,equals方法一定返回true吗?
② 两个对象A、B 两个对象equals方法返回true,两个对象的哈希值一定相同吗?
结论 : 两个对象的哈希值相同,不要求equals一定返回true. 两个对象的equals返回true,两个对象的哈希值必须一致
Sun 公司官方规定 : 上面的结论
8.6 哈希表的数据结构
数组 + 链表的组合体
class Node{ E element; //存储的元素 Node next; //下一个元素 } main(){ Node[] node = new Node[5]; }
- 哈希表的底层数组长度默认是16个,扩容为原来长度的2倍
- 加载因子默认是0.75F,数组中存储元素的个数达到长度的75%就扩容
8.7 哈希表存储对象的过程
public static void main(String[] args) { Set<String> set = new HashSet<String>(); //存储对象 set.add("abc"); set.add("bbc"); set.add(new String("abc")); set.add("通话"); set.add("重地"); System.out.println("set = " + set); }
8.8 哈希表存储自定义的对象
需要重写自定义类的hashCode()和equal()两个方法
public class Student { private int age; private String name; public Student(){} public Student( String name,int age) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; } @Override public int hashCode() { int result = age; result = 31 * result + (name != null ? name.hashCode() : 0); return result; } @Override public String toString() { return "Student{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
public static void main(String[] args) { Set<Student> set = new HashSet<Student>(); //存储Student的对象 set.add(new Student("a1",201)); set.add(new Student("a2",202)); set.add(new Student("a2",202)); set.add(new Student("a3",203)); set.add(new Student("a4",204)); System.out.println("set = " + set); }
8.9 哈希表源码
HashSet集合本身不具备任何功能,内部调用了另一个集合对象HashMap
- 构造方法无参数
public HashSet() { map = new HashMap<>(); }
- HashMap类的成员变量
//哈希表数组的初始化容量,16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16
static final int MAXIMUM_CAPACITY = 1 << 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;//阈值,转红黑树
- HashMap内部类Node
//节点 static class Node<K,V> implements Map.Entry<K,V> { final int hash; //对象哈希值 final K key; //存储的对象 V value; //使用Set的集合,value没有值 Node<K,V> next; //链表的下一个节点 }
- Set集合存储方法add(),调用的是HashMap集合的方法put()【大致了解就行】
//HashMap存储对象的方法put,Key存储的元素,V是空的对象 public V put(K key, V value) { //存储值,传递新计算哈希值,要存储的元素 return putVal(hash(key), key, value, false, true); }
//传递存储的对象,再次计算哈希值 //尽量降低哈希值的碰撞 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
//存储值,重写计算的哈希值,要存储值 final V putVal(int hash, K key, V value, boolean false, boolean true) { //Node类型数组, Node类型数组 n, i Node<K,V>[] tab; Node<K,V> p; int n, i; //tab =Node[]=null if ((tab = table) == null || (n = tab.length) == 0){ //n=赋值为 tab数组=resize()方法返回数组,默认长度的数组16 n = (tab = resize()).length;// 16 //数组的长度-1 & 存储对象的哈希值,确定存储的位置 //判断数组的索引上是不是空的 if ((p = tab[i = (n - 1) & hash]) == null) //数组索引 赋值新的节点对象,传递计算的哈希值,存储的对象 tab[i] = newNode(hash, key, value, null); else{ //数组的索引不是空,要存储的对象,已经有了 //判断已经存在的对象,和要存储对象的哈希值和equals方法 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) //遍历该索引下的链表,和每个元素比较hashCode和equals } } }
8.10 哈希表面试的问题
JDK7版本和JDK8版本的哈希表的区别
- JDK7没有转红黑树
- JDK8转成红黑树
- 转成树的两个参数
- 当一个数组中存储的链表长度>=8 转树
- 数组的整体长度超过64
- 树转回链表
- 链表的长度 <=6
- JDK7元素采用头插法,JDK8元素采用尾插法
9. 红黑树
红黑树(Red-Black-Tree)
- 二叉树,本质就是链表
- 查询速度快
- 每个一个节点,只有两个子节点,左和右
- 树长偏了
- 自然平衡二叉树
- 二叉树的基础上,改进,保证树是平衡的
- 红黑树
- 每个节点有颜色,要么红,要么是黑
- 根节点必须是黑色
- 叶子节点必须是黑色
- 变量表示颜色,true黑色,false红色
9.1 TreeSet集合使用
TreeSet集合,底层是红黑树结构,依赖于TreeMap的实现
红黑树特点查找速度快,线程不安全
可以对存储到红黑树的元素进行排序,元素的自然顺序 abcd.. 字典顺序
public static void treeSetString(){ Set<String> set = new TreeSet<>(); //存储元素 set.add("abcd"); set.add("ccdd"); set.add("z"); set.add("wasd"); set.add("bbaa"); System.out.println("set = " + set); }
9.2 TreeSet存储自定义对象
/** * TreeSet集合存储Student对象 */ public static void treeSetStudent(){ Set<Student> set = new TreeSet<Student>(); set.add(new Student("a",10)); set.add(new Student("b",20)); System.out.println("set = " + set); }
程序出现了异常,类型的转换异常 ClassCastException
异常原因,Student类不能进行类型的转换,有接口没有实现java.lang.Comparable.
类实现接口Comparable,这个类就具有了自然顺序。
- Student类具有自然顺序
- 实现接口Comparable,重写方法compareTo
/** * 重写方法compareTo * 返回int类型 * 参数 : 要参与比较的对象 * this对象和student对象 * * 红黑树,后来的对象是this,原有的对象是参数 */ public int compareTo(Student student){ return this.age - student.age; }
- 自定义比较器
- java.util.Comparator接口
/** * 自定义的比较器 * 实现接口,重写方法 */ public class MyCom implements Comparator<Student> { @Override /** * TreeSet集合自己调用方法 * 传递参数 * Student o1, Student o2 * o1是后来的对象 * o2是已经有的对象 */ public int compare(Student o1, Student o2) { return o1.getAge() - o2.getAge(); } } Set<Student> set = new TreeSet<Student>( new MyCom());
图三:TreeSet集合的构造器
10. LinkedHashSet
底层的数据结构是哈希表,继承HashSet
LinkedHashSet数据是双向链表有序的集合,存储和取出的顺序一样
public static void main(String[] args) { Set<String> set = new LinkedHashSet<>(); set.add("b"); set.add("e"); set.add("c"); set.add("a"); set.add("d"); System.out.println("set = " + set); }
11. Collections工具类
- java.util.Collection 集合的顶级接口
- java.util.Collections 操作集合的工具类
- 工具类的方法全部静态方法,类名直接调用
- 主要是操作Collection系列的单列集合,少部分功能可以操作Map集合
/** * 集合操作的工具类 * Collections * 工具类有组方法: synchronized开头的 * * 传递集合,返回集合 * 传递的集合,返回后,变成了线程安全的集合 */ public class CollectionsTest { public static void main(String[] args) { sort2(); } //集合元素的排序,逆序 public static void sort2(){ List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(15); list.add(5); list.add(20); list.add(9); list.add(25); System.out.println("list = " + list); //Collections.reverseOrder() 逆转自然顺序 Collections.sort(list,Collections.reverseOrder()); System.out.println("list = " + list); } //集合元素的排序 public static void sort(){ List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(15); list.add(5); list.add(20); list.add(9); list.add(25); System.out.println("list = " + list); Collections.sort(list); System.out.println("list = " + list); } //集合元素的随机交换位置 public static void shuffle(){ List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(15); list.add(5); list.add(20); list.add(9); list.add(25); System.out.println("list = " + list); Collections.shuffle(list); System.out.println("list = " + list); } //集合的二分查找 public static void binarySearch(){ List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(5); list.add(9); list.add(15); list.add(20); list.add(25); int index = Collections.binarySearch(list, 15); System.out.println(index); } }
12. 泛型 Generic
泛型技术是JDK版本一大升级,源自于JDK1.5
泛型就是集合类<泛型>
//无泛型写法 public static void main(String[] args) { /** * JDK没有泛型技术,就是这样写 * 集合可以存储任何数据类型 * 添加元素的数据类型是Object */ List list = new ArrayList(); list.add("a"); list.add(1); Iterator it = list.iterator(); while (it.hasNext()){ Object obj = it.next();//不能类型转换 System.out.println(obj); } }
12.1 泛型的安全机制
软件升级:安全性提高,修复Bug错误,改善用户体验,增加功能,提升性能
JDK1.5里程碑版本
泛型作用:强制了集合存储固定的数据类型
泛型的书写格式:
集合类<存储的数据类型> 变量名 = new 集合类<存储的数据类型>(); 类型可以不写:钻石操作符
加入泛型后,程序的安全性提升了
public static void main(String[] args) { /** * JDK没有泛型技术,就是这样写 * 集合可以存储任何数据类型 * 添加元素的数据类型是Object */ List<String> list = new ArrayList<String>(); list.add("a"); list.add(1); //编译错误,数据类型不匹配 Iterator<String> it = list.iterator(); while (it.hasNext()){ String obj =it.next(); //类型转换不需要 System.out.println(obj); } }
- 使用泛型的好处:
- 安全性提高了
- 程序的代码量减少
- 避免了类型的强制转换
- 程序的问题,由运行时期,提前到编译时期
12.2 泛型中的 E 问题
E没有什么实际价值,只是一个变量而已
特殊:等待接收指定的数据类型
//ArrayList<E> //创建对象 ArrayList<String> al = new ArrayList<String>(); //E 不在是E了,变成String public boolean add(String e) { }
12.3 自定义泛型类
/** * 定义类,类名叫工厂 * 自定义泛型类 * Factory<什么都可以写> 只是变量名而已 */ public class Factory<QQ> { private QQ q; public void setQ(QQ q){ this.q = q; } public QQ getQ(){ return q; } }
public static void main(String[] args) { //创建对象Factory类对象 // Factory factory = new Factory();//没有泛型,QQ就是Object Factory<String> factory = new Factory<String>(); factory.setQ("abc"); String s = factory.getQ(); System.out.println(s); Factory<Double> factory2 = new Factory<Double>(); factory2.setQ(1.5); Double q = factory2.getQ(); System.out.println(q); }
12.4 泛型方法
/** * 泛型的方法,方法参数上 */ public class Factory<Q> { /* * 静态方法 * Q是非静态的, Q的数据类型,是new的时候指定的 * * 静态方法参数中的泛型,不能和类一样 * 静态方法的泛型,需要在方法上单独定义 * 写在返回值类型的前面 */ public static <T> void staticMethod(T q){ System.out.println(q); } public void print(Q q){ System.out.println(q); } }
12.5 泛型接口
- 实现类实现接口,不实现泛型
- 实现类实现接口,同时指定泛型
//泛型接口 public interface Inter <T> { public abstract void inter(T t); }
/** * 实现接口,不理会泛型 * 对象创建的时候,指定类型 */ public class InterImpl<T> implements Inter<T>{ public void inter(T t){ System.out.println(t); } }
/** * 实现接口,同时指定泛型 */ public class InterImpl2 implements Inter<String> { public void inter(String s) { System.out.println("s=="+s); } }
public class GenericTest { public static void main(String[] args) { Inter<String> in = new InterImpl<String>(); in.inter("ok"); Inter in2 = new InterImpl2(); in2.inter("kkk"); } }
实现接口,但是不实现泛型,会在调用方法传入参数时才确认数据类型,即参数类型会变成Object。
12.6 泛型通配符
?指可以是任意引用类型
//泛型的通配符 public class GenericTest { public static void main(String[] args) { List<String> stringList = new ArrayList<String>(); stringList.add("abc"); stringList.add("bbc"); List<Integer> integerList = new ArrayList<Integer>(); integerList.add(1); integerList.add(2); each(stringList); each(integerList); } /** * 定义方法,可以同时迭代器 遍历这两个集合 * 方法的参数,是要遍历的集合,不确定是哪个集合 * 定义参数,写接口类型,不要写实现类 */ public static void each(List<?> list){ Iterator<?> it = list.iterator(); while (it.hasNext()){ Object obj = it.next(); System.out.println(obj); } } }
12.7 泛型限定
泛型限定:限制的是数据类型
- <? extends Company> 传递类型可以是Company或者是他的子类
- <? extends E>传递E类型或者是E的子类,泛型上限限定
- <? super E >传递E类型或者是E的父类,泛型下限限定
public static void main(String[] args) { //创建集合,存储员工对象 //开发部的 List<Development> devList = new ArrayList<Development>(); //存储开发部员工对象 Development d1 = new Development(); d1.setName("张三"); d1.setId("开发部001"); Development d2 = new Development(); d2.setName("张三2"); d2.setId("开发部002"); devList.add(d1); devList.add(d2); //财务部集合 List<Financial> finList = new ArrayList<Financial>(); Financial f1 = new Financial(); f1.setName("李四"); f1.setId("财务部001"); Financial f2 = new Financial(); f2.setName("李四2"); f2.setId("财务部002"); finList.add(f1); finList.add(f2); System.out.println(devList); System.out.println(finList); each(devList); each(finList); // List<Integer> integerList = new ArrayList<>(); // integerList.add(1); // each(integerList); } /** * 要求 : 定义方法 * 同时遍历2个集合 * 遍历的同时取出集合元素,调用方法work() * ? 接收任何一个类型 * 只能接收 Company和子类对象 * 明确父类,不能明确子类 */ public static void each(List<? extends Company> list){ Iterator<? extends Company> it = list.iterator(); while (it.hasNext()){ //取出元素 Company obj =it.next(); obj.work(); } }
13. 增强型的for循环
JDK1.5出现的特性:循环的特性 (少些代码)
Collection是单列集合的顶级接口,但是到JDK1.5后,为Collection找了个爹
java.lang.Iterable接口:实现接口,就可以成为 "foreach"语句的目标
Collection、List、Set都继承了接口,包括数组
Iterable接口定义了foreach()和iterator()两个方法
13.1 for的格式
for(数据类型 变量名 : 集合或者数组){}
- 遍历数组
/** * for循环遍历数组 * for(数据类型 变量名 : 集合或者数组){} */ public static void forArray(){ int[] arr = {1,3,5,7,9}; for(int i : arr){ System.out.println(i+1); } System.out.println("arr=="+arr[0]); }
- 遍历集合
/* for循环遍历集合 */ public static void forList(){ List<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); for(String s : list){ System.out.println(s); } }
14. Map接口
java.util.Map接口,是双列集合的顶级接口。
Map集合容器每次存储2个对象,一个对象称为键(Key),一个对象称为值(Value)。
在一个Map的集合容器中,键保证唯一性,不包含重复键,每个键只能对应一个值
14.1 Map接口方法
- V put(K,V)存储键值对,存储重复键,返回被覆盖之前的值
/** * put方法,存储键值对 * Map接口的实现类HashMap */ public static void mapPut(){ //创建对象,指定键的数据类型,值的数据 Map<String,Integer> map = new HashMap<String,Integer>(); map.put("a",1); map.put("b",2); map.put("c",3); map.put("d",4); //相同键,返回被覆盖的值 Integer value = map.put("c",5); System.out.println("map = " + map); System.out.println("value = " + value); }
- V get(K)通过键获取值,参数传递键,找这个键对应的值,没有这个键返回null
/** * V get(K)通过键获取值,参数传递键,找这个键对应的值,没有这个键返回null */ public static void mapGet(){ //创建对象,指定键的数据类型,值的数据 Map<String,Integer> map = new HashMap<String,Integer>(); map.put("a",1); map.put("b",2); map.put("c",3); map.put("d",4); //键找值 Integer value = map.get("f"); System.out.println(value); }
- boolean containsKey(K)判断集合是否包含这个键,包含返回true
- boolean containsValue(V)判断集合是否包含这个值,包含返回true
- int size() 返回集合长度,Map集合中键值对的个数
- V remove(K)移除指定的键值对,返回被移除之前的值
- Collection<V> values() Map集合中的所有的值拿出,存储到Collection集合
/*boolean containsKey(K)判断集合是否包含这个键,包含返回true - boolean containsValue(V)判断集合是否包含这个值,包含返回true - int size() 返回集合长度,Map集合中键值对的个数 - V remove(K)移除指定的键值对,返回被移除之前的值 - Collection<V> values() Map集合中的所有的值拿出,存储到Collection集合 */ public static void mapMethod(){ //创建集合,键是整数,值是String Map<Integer,String> map = new HashMap<Integer, String>(); map.put(1,"a"); map.put(2,"b"); map.put(3,"c"); map.put(4,"d"); map.put(5,"e"); //boolean containsKey(K)判断集合是否包含这个键,包含返回true boolean b = map.containsKey(1); System.out.println("集合中包含键:"+b); //boolean containsValue(V)判断集合是否包含这个值,包含返回true b = map.containsValue("c"); System.out.println("集合中包含值:"+b); //size()返回集合的长度 int size = map.size(); System.out.println("集合长度:"+size); //V remove(K)移除指定的键值对,返回被移除之前的值 String value = map.remove(1); System.out.println("被删除之前的:"+value); System.out.println(map); //Collection<V> values() Map集合中的所有的值拿出,存储到Collection集合 Collection<String> coll = map.values(); for(String s : coll){ System.out.println(s); } }
14.2 Map集合的遍历——键找值
- 实现思想 :
- Map接口定义了方法 keySet() 所有的键,存储到Set集合
- 遍历Set集合
- 取出Set集合元素 Set集合的元素是Map集合的键
- Map集合方法get()传递键获取值
/** * - Map接口定义了方法 keySet() 所有的键,存储到Set集合 * - 遍历Set集合 * - 取出Set集合元素 **Set集合的元素是Map集合的键** * - Map集合方法get()传递键获取值 */ public static void mapKeySet(){ Map<String,String> map = new HashMap<String, String>(); map.put("a","java"); map.put("b","c++"); map.put("c","php"); map.put("d","python"); map.put("e","erlang"); //Map接口定义了方法 keySet() 所有的键,存储到Set集合 Set<String> set = map.keySet(); //遍历Set集合 Iterator<String> it = set.iterator(); //取出Set集合元素 **Set集合的元素是Map集合的键** while (it.hasNext()){ String key = it.next(); //Map集合方法get()传递键获取值 String value = map.get(key); System.out.println(key+"==="+value); } }
14.3 Map集合的遍历——键值对映射关系
- 实现思想 :
- Map接口的方法 Set< Map.Entry<Key,Value> > entrySet()【泛型可以多个表示】
- 方法返回Set集合,集合中存储的元素,比较特别
- 存储的是Map集合中,键值对映射关系的对象,内部接口 Map.Entry
- 遍历Set集合
- 取出Set集合的元素
- 是Map.Entry接口对象
- 接口的对象方法:getKey(),getValue()
public static void mapEntrySet(){ Map<String,String> map = new HashMap<String, String>(); map.put("a","java"); map.put("b","c++"); map.put("c","php"); map.put("d","python"); map.put("e","erlang"); //Map接口的方法 Set< Map.Entry<Key,Value> > entrySet() Set<Map.Entry<String,String>> set = map.entrySet(); //- 遍历Set集合 Iterator<Map.Entry<String,String>> it = set.iterator(); while (it.hasNext()){ //取出Set集合的元素 Map.Entry<String,String> entry = it.next(); //- 接口的对象方法: getKey() ,getValue() String key = entry.getKey(); String value = entry.getValue(); System.out.println(key +"==="+ value); } }
15. HashMap 实现类
- HashMap集合特点
- 是哈希表结构【Set和Map的底层数据结构都是是哈希表】
- 保证键唯一性,用于键的对象,必须重写hashCode,equals方法【伞公司写的hashCode方法不对外开放的,所以不知道写了什么。但是能确定的一点是,只要是桐哥类建立的对象,他们的哈希值都是一样的。所以保证键唯一性需要重写hashCode,equals方法】
- 线程不安全集合,运行速度快
- 集合运行使用null,作为键或者值
/** * HashMap集合 * 键是Person,值是String */ public static void hashMap2(){ Map<Person,String> map = new HashMap<Person, String>(); map.put(new Person("a",20),"广东"); map.put(new Person("b",22),"香港"); map.put(new Person("b",22),"贵港"); map.put(new Person("c",24),"澳门"); map.put(new Person("d",26),"深圳"); System.out.println("map = " + map); } /** * HashMap集合 * 键是字符串,值是Person */ public static void hashMap1(){ Map<String, Person> map = new HashMap<String, Person>(); map.put("a",new Person("张三",20)); map.put("b",new Person("张三",20)); map.put("c",new Person("张三",20)); map.put(null,null); //Set<String> set = map.keySet(); for(String key : map.keySet()){ //Person person = map.get(key); System.out.println(key+"==="+map.get(key)); } System.out.println("=============="); //Set<Map.Entry<String,Person>> set = map.entrySet(); for(Map.Entry<String,Person> entry : map.entrySet()){ System.out.println(entry.getKey()+"==="+entry.getValue()); }
16. Hashtable 实现类
Map接口的实现类Hashtable,Hashtable类诞生于JDK1.0版本,Map接口诞生于JDK1.2版本。 Hashtable类从JDK1.2开始,改进为实现Map接口。
- Hashtable类的特点
- 底层数据结构是哈希表
- 线程安全的,运行速度慢,被更加先进的HashMap取代
- 不允许null值,null键,存储null直接抛出空指针异常。
17. LinkedHashMap 实现类
LinkedHashMap继承HashMap实现Map接口,LinkedHashMap底层实现原理是哈希表,双向链,存取有序。其它的特性和父类HashMap一样。
public static void main(String[] args) { Map<String,String> map = new LinkedHashMap<String, String>(); map.put("aa","qq"); map.put("123","qq"); map.put("bbb","qq"); System.out.println(map); }
18. Vector 实现类
List接口的实现Vector,命运和Hashtable一样。
- Vector类的特点
- 底层实现结构是数组
- 数组的默认容量是10,每次扩容是原来的长度*2
- 线程安全,运行速度慢,被ArrayList取代
19. TreeMap 实现类
- TreeMap集合的特点
- 底层实现是红黑树结构 (添加查询速度比较快)
- 存储到TreeMap中元素,对键进行排序
- 排序依据 :
- 对象的自然顺序,作为键的对象,实现了接口Comparable
- 自己提供比较器,实现接口Comparator,优先级高
- 线程不安全的,运行速度快
/** * TreeMap集合存储对象 * Student作为键,字符串是值 * 自定义的比较器排序 */ public static void treeMap2(){ Map<Student,String> map = new TreeMap<Student, String>( new MyCom() ); map.put(new Student("a",20),"广东"); map.put(new Student("b",19),"广西"); System.out.println("map = " + map); } /** * TreeMap集合存储对象 * Person作为键,字符串是值 */ public static void treeMap1(){ Map<Person,String> map = new TreeMap<Person, String>(); map.put(new Person("a",20),"广东"); map.put(new Person("b",19),"广西"); System.out.println("map = " + map); }
/** * 自定义的比较器,实现接口 Comparator */ class MyCom implements Comparator<Student>{ /** * 方法compare 是TreeMap调用 * 传递参数,后来的对象传递到s1, 已经有的对象传递到s2 */ public int compare(Student s1, Student s2){ return s1.getAge() - s2.getAge(); } }
/** * 进行比较: * compareTo方法由,集合TreeMap调用 * 传递相关的参数 集合中后来的对象是this,先来的对象是参数 p */ public int compareTo(Person p){ return this.age - p.age; }
20. Properties 实现类
- Properties集合特点
- 继承Hashtable,实现Map接口
- 底层是哈希表结构
- 线程是安全的,运行速度慢
- 集合没有泛型的写法,键和值的数据类型锁定为String类型
- 集合有自己的特有方法
- 此集合可以和IO流对象结合使用,实现数据的持久存储
- 方法和IO相关:load(输入流)
/** * 集合遍历 * Properties类的方法 stringPropertyNames() [等效于map.keySet()] 返回Set集合 * Set集合存储的是 Properties集合的所有键 */ public static void prop3(){ Properties prop = new Properties(); prop.setProperty("a","1"); prop.setProperty("b","2"); prop.setProperty("c","3"); Set<String> set = prop.stringPropertyNames(); for(String key : set){ System.out.println(key +"=="+ prop.getProperty(key)); } } /** * 集合取出元素 * Properties集合取出方法 getProperty(String key) */ public static void prop2(){ Properties prop = new Properties(); prop.setProperty("a","1"); prop.setProperty("b","2"); prop.setProperty("c","3"); System.out.println(prop); String value = prop.getProperty("a"); System.out.println(value); } /** * 集合存储键值对 * Map接口,存储方法put * Properties集合存储方法 setProperty(String key,String value) */ public static void prop1(){ Properties prop = new Properties(); prop.setProperty("a","1"); prop.setProperty("b","2"); prop.setProperty("c","3"); System.out.println(prop); }