集合(四)
HashMap和Hashtable的区别
HashMap的key可以为null吗?value呢?都可以为空!
package se3.themap;
import java.util.HashMap;
import java.util.Map;
public class HashMapTest03 {
public static void main(String[] args) {
Map map = new HashMap();
//HashMap集合允许key为null
map.put(null,null);
System.out.println(map.size());//1
//key重复的话,value会覆盖
map.put(null,100);
System.out.println(map.size());//1
//通过key获取value
System.out.println(map.get(null));//100
}
}
Hashtable的key可以为null吗?value呢?都不能为空!
package se3.themap;
import java.util.Hashtable;
import java.util.Map;
public class HashTableTest01 {
public static void main(String[] args) {
Map map = new Hashtable();
map.put(null,"阿波");
map.put(1,null);
}
}
- Hashtable和HashMap一样,底层都是哈希表数据结构
- Hashtable方法都带有synchronized:线程安全的
- Hashtable的初始化容量是11,默认加载因子是0.75
- Hashtable的扩容:原容量*2再+1
Properties
目前只需要掌握Properties属性类对象的相关方法即可
Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型
Properties被称为属性类对象
Properties是线程安全的
package se3.themap;
import java.util.Properties;
public class PropertiesTest01 {
public static void main(String[] args) {
//创建一个Properties对象
Properties pro = new Properties();
//需要掌握Properties的两个方法,一个存,一个取
pro.setProperty("url","jdbc:mysql://localhost:3306/bjpowernode");
pro.setProperty("driver","com.mysql.jdbc.Driver");
pro.setProperty("username","root");
pro.setProperty("password","123");
//通过key获取value
String url = pro.getProperty("url");
String driver = pro.getProperty("driver");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println(url);//jdbc:mysql://localhost:3306/bjpowernode
System.out.println(driver);//com.mysql.jdbc.Driver
System.out.println(username);//root
System.out.println(password);//123
}
}
TreeSet
- TreeSet集合底层实际上是一个TreeMap
- TreeMap集合底层是一个二叉树
- 放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了
- TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序,称为可排序集合。
package se3.set;
import java.util.TreeSet;
public class TreeSetTest02 {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<String> ts = new TreeSet<>();
//添加String元素
ts.add("钱七");
ts.add("张三");
ts.add("赵六");
ts.add("李四");
ts.add("王五");
//遍历
for (String s : ts){
//按照字典顺序,升序
System.out.print(s + " ");
}//张三 李四 王五 赵六 钱七
System.out.println();
TreeSet<Integer> ts2 = new TreeSet<>();
ts2.add(100);
ts2.add(200);
ts2.add(900);
ts2.add(800);
ts2.add(600);
ts2.add(10);
for (Integer elt : ts2){
//升序
System.out.print(elt + " ");
}//10 100 200 600 800 900
}
}
关于自定义类型排序
对自定义的类型来说,TreeSet可以排序吗?不行
- 以下程序中对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则
- 谁大谁小并没有说明
- 出现异常的原因是:Person类没有实现java.lang.Comparable接口。
package se3.set;
import java.util.TreeSet;
public class TreeSetTest03 {
public static void main(String[] args) {
Person p1 = new Person(32);
Person p2 = new Person(20);
Person p3 = new Person(30);
Person p4 = new Person(25);
//添加元素
TreeSet<Person> persons = new TreeSet<>();
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
//遍历
for (Person p : persons){
System.out.print(p + " ");
}
/**运行结果:
* Exception in thread "main" java.lang.ClassCastException: se3.set.Person cannot be cast to java.lang.Comparable
* at java.util.TreeMap.compare(TreeMap.java:1294)
* at java.util.TreeMap.put(TreeMap.java:538)
* at java.util.TreeSet.add(TreeSet.java:255)
* at se3.set.TreeSetTest03.main(TreeSetTest03.java:18)
*/
}
}
class Person{
int age;
public Person(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "age=" + age + '}';
}
}
自定义规则后如下:
package se3.set;
import java.util.TreeSet;
public class TreeSetTest04 {
public static void main(String[] args) {
Person2 p1 = new Person2(32);
Person2 p2 = new Person2(20);
Person2 p3 = new Person2(30);
Person2 p4 = new Person2(25);
//添加元素
TreeSet<Person2> persons = new TreeSet<>();
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
//遍历
for (Person2 p : persons){
System.out.println(p);
}
/**
* 运行结果:
* Person{age=20}
* Person{age=25}
* Person{age=30}
* Person{age=32}
*/
}
}
/*
放在TreeSet集合中元素需要实现java.lang.Comparable接口
并且实现CompareTo方法,equals可以不写
*/
class Person2 implements Comparable<Person2>{
int age;
public Person2(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "age=" + age + '}';
}
//需要在这个方法中编写比较的逻辑,或者说比较的方法,按什么进行比较
//k.compareTo(t.key)
//拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 =0
//比较规则是由程序员指定的,例如按照年龄升序或者降序
@Override
public int compareTo(Person2 p) {//p1.compareTO(p2)
//this就是p1
//p是p2
//p1和p2比较的时候,就是this和p比较
/*
int age1 = this.age;
int age2 = p.age;
if(age1 == age2){
return 0;
} else if(age1 > age2){
return 1;
} else{
return -1;
}
*/
//简写
return this.age - p.age;
}
}
关于比较规则
怎么写?先按照年龄升序,如果年龄一样再按照姓名升序,例子如下:
package se3.set;
import java.util.TreeSet;
//先按照年龄升序,如果年龄一样再按照姓名升序
public class TreeSetTest05 {
public static void main(String[] args) {
TreeSet<Vip> vips = new TreeSet<>();
vips.add(new Vip("a阿波",20));
vips.add(new Vip("b阿波",20));
vips.add(new Vip("波波",18));
vips.add(new Vip("小波",17));
for (Vip vip : vips){
System.out.println(vip);
}
/**
* 输出结果:
* Vip{name='小波', age=17}
* Vip{name='波波', age=18}
* Vip{name='a阿波', age=20}
* Vip{name='b阿波', age=20}
*/
}
}
class Vip implements Comparable<Vip>{
String name;
int age;
public Vip(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Vip{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*
compareTo方法的返回值很重要:
返回0表示相同,value会覆盖
返回>0,会继续在右子树上找【10-9=1,1>0的说明左边这个数字比较大,所以在右子树上找】
返回<0,会继续在左子树上找
*/
@Override
public int compareTo(Vip v) {
if(this.age == v.age){
//年龄相同时按照名字排序
//名字是String类型,可以直接比,调用compareTo完成比较
return this.name.compareTo(v.name);
} else {
//年龄不一样
return this.age - v.age;
}
}
}
自平衡二叉树数据结构
TreeSet/TreeM是自平衡二叉树,遵循左小右大原则存放(存放的时候要进行比较)
注意:存放的过程就是排序的过程,取出来就是自动按照大小顺序排列
100 200 50 60 80 120 140 130 135 180 666 40 55存放后如下图
遍历二叉树的时候有三种方式
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
TreeSet集合/TreeMap集合采用的是:中序遍历
Iterator迭代器采用的是中序遍历方式(左根右)
中序遍历
采用中序遍历取出:
40 50 55 60 80 100 120 140 130 135 180 200 666
比较器方式实现TreeSet排序
package se3.set;
import java.util.Comparator;
import java.util.TreeSet;
/*
TreeSet集合中元素可排序的第二种方式:使用比较器的方式
*/
public class TreeSetTest06 {
public static void main(String[] args) {
//创建TreeSet集合的时候,需要使用这个比较器
//TreeSet<WuGui> wuGuis = new TreeSet<>();//这样不行,没有通过构造方法传递一个比较器进去
//给构造方法传递一个比较器
TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator());
wuGuis.add(new WuGui(1000));
wuGuis.add(new WuGui(800));
wuGuis.add(new WuGui(810));
for (WuGui wuGui : wuGuis){
System.out.println(wuGui);
}
/**
* 输出结果:
* 乌龟[age=800]
* 乌龟[age=810]
* 乌龟[age=1000]
*/
}
}
//乌龟
class WuGui{
int age;
public WuGui(int age) {
this.age = age;
}
@Override
public String toString() {
return "乌龟[" +
"age=" + age +
']';
}
}
//单独在这里编写一个比较器
//比较器实现java.util.Comparator接口。(Comparable是java.lang包下的,Comparator是java.util包下的)
class WuGuiComparator implements Comparator<WuGui>{
@Override
public int compare(WuGui o1, WuGui o2) {
//指定比较规则,按照年龄排序
return o1.age - o2.age;
}
}
匿名内部类方式实现TreeSet排序
package se3.set;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest06 {
public static void main(String[] args) {
//TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator());
//使用匿名内部类的方式
TreeSet<WuGui> wuGuis = new TreeSet<>(new Comparator<WuGui>() {
@Override
public int compare(WuGui o1, WuGui o2) {
return o1.age - o2.age;
}
});
wuGuis.add(new WuGui(1000));
wuGuis.add(new WuGui(800));
wuGuis.add(new WuGui(810));
for (WuGui wuGui : wuGuis){
System.out.println(wuGui);
}
/**
* 输出结果:
* 乌龟[age=800]
* 乌龟[age=810]
* 乌龟[age=1000]
*/
}
}
//乌龟
class WuGui{
int age;
public WuGui(int age) {
this.age = age;
}
@Override
public String toString() {
return "乌龟[" +
"age=" + age +
']';
}
}
结论
放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:
- 放在集合中元素实现java.lang.Comparable接口
- 在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象
Comparable和Comparator怎么选择呢?
当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议使用Comparable接口。
如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
Collections工具类
package se3.collection;
import java.util.*;
/*
java.util.Collection 集合接口
java.util.Collections 集合工具类,方便集合的操作。
*/
public class CollectionsTest01 {
public static void main(String[] args) {
//ArrayList集合不是线程安全的
List<String> list = new ArrayList<>();
//变成线程安全的
Collections.synchronizedList(list);
list.add("abf");
list.add("abx");
list.add("abc");
list.add("abe");
//排序
Collections.sort(list);
for (String s : list){
System.out.print(s + " ");
}//abc abe abf abx
System.out.println();
List<WuGui2> wuGui2s = new ArrayList<>();
wuGui2s.add(new WuGui2(1000));
wuGui2s.add(new WuGui2(8000));
//注意:对List集合中元素排序,需要保证List集合中的元素实现Comparable接口
Collections.sort(wuGui2s);
for (WuGui2 wuGui2 : wuGui2s){
System.out.println(wuGui2);
}
/*
乌龟[age=1000]
乌龟[age=8000]
*/
//Set集合排序
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);
Collections.sort(mylist);
for (String s : mylist){
System.out.print(s + " ");
}//king king1 king2 kingsoft
}
}
class WuGui2 implements Comparable<WuGui2>{
int age;
public WuGui2(int age) {
this.age = age;
}
@Override
public int compareTo(WuGui2 o) {
return this.age - o.age;
}
@Override
public String toString() {
return "乌龟[" +
"age=" + age +
']';
}
}