数据结构:Map
本章所涉及到的数据结构知识可在数据结构学习记录中学习:
咖啡ice的数据结构学习记录
1.Map常用方法
-
Map和Collection没有继承关系。
-
Map集合以key和value的方式存储数量:键值对。
key起主导地位,value是key的附属品。
-
Map接口中常用方法:
Map支持泛型:Map<K,V>
向Map中放键值对:V put(K key,V value)
通过key获取value:V get(Object key)
清空Map:void clear()
判断是否包含某个key:boolean containsKey(Object key)
判断是否包含某个value:boolean containsValue(Object value)
判断集合是否为空:boolean isEmpty()
获取集合中所有的key,所有的健是一个Set集合:Set<K> keySet()
获取集合中所有的value:Collection<V> values()
通过key删除键值对:V remove(Object key)
返回Map集合中键值个数:int size()
将Map集合转化为Set集合:Set<Map.Entry<K,V>> entrySet()
public class MapTest {
public static void main(String[] args) {
//创建Map集合对象
Map<Integer,String> map = new HashMap<>();
//向Map集合中添加键值对
map.put(1,"ice");
map.put(2,"coffee");
map.put(3,"is");
map.put(4,"nice");
//通过key获取value
System.out.println(map.get(2)); //coffee
//获取键值对的数量
System.out.println("键值对数量:" + map.size()); //键值对数量:4
//通过key删除key-value
map.remove(1);
System.out.println("键值对数量:" + map.size()); //键值对数量:3
//判断是否包含某个key
System.out.println(map.containsKey(2)); //true
//判断是否包含某个value
System.out.println(map.containsValue("coffee")); //true
//获取所有的value
Collection<String> values = map.values();
for (String value:
values) {
System.out.printf(value + " "); //coffee is nice
}
System.out.println();
//清空map
map.clear();
System.out.println("键值对数量:" + map.size()); //键值对数量:0
//判断集合是否为空
System.out.println(map.isEmpty());//true
}
}
-
Map集合遍历
方法一:获取所有的key通过遍历key来遍历value。
public class MapTest {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"ice");
map.put(2,"coffee");
map.put(3,"is");
map.put(4,"nice");
//遍历map集合:获取所有的key。
Set<Integer> keys = map.keySet();
//遍历key,通过key获取value
for (Integer key:
keys) {
System.out.printf(key + "=" + map.get(key) + " "); //1=ice 2=coffee 3=is 4=nice
}
}
}
方法二:Set<Map.Entry<K,V>> entrySet()
这个方法就是把Map集合直接全部转换成Set集合。Set集合的类型是Map.Entry
public class MapTest {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"ice");
map.put(2,"coffee");
map.put(3,"is");
map.put(4,"nice");
Set<Map.Entry<Integer,String>> set = map.entrySet();
//遍历Set集合
for (Map.Entry<Integer,String> s:
set) {
//System.out.printf(s.getKey() + "=" + s.getValue() + " ");
System.out.printf(s + " "); //1=ice 2=coffee 3=is 4=nice
}
}
}
2. HashMap
-
HashMap集合底层是哈希表/散列表的数据结构。不可重复,如果key重复了,value会直接覆盖。
常用方法:
map.put(k,v)
v = map.get(k)
-
如果所有的hashCode()方法返回值固定某个值,会导致底层哈希表变成了纯单项链表。
-
放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
-
HashMap中key部分元素不可重复。
public class HashMapTest { public static void main(String[] args) { //Integer是key,其hashCode和equals都重写了 Map<Integer,String> map = new HashMap<>(); map.put(1111,"ice"); map.put(6666,"coffee"); map.put(7777,"is"); map.put(2222,"very"); map.put(2222,"good"); //key重复时,value会自动覆盖 System.out.println(map.size()); //4 //遍历Map集合 Set<Map.Entry<Integer,String>> set = map.entrySet(); for (Map.Entry<Integer,String> entry: set) { //hashMap集合中key部分元素,不可重复。 System.out.printf(entry.getKey() + "=" + entry.getValue() + " "); //7777=is 1111=ice 6666=coffee 2222=good } } }
-
HashMap集合的个数必须是2的倍数,能够提高HashMap的存储效率。
-
HashMap集合key部分的元素是放到了HashSet集合中,所以HashSet集合的的元素需要同时重写hashCode()和equals()方法
-
如果一个方法的equals()方法重写了,必须重写hashCode(),如果equals()方法返回的是true,hashCode()返回值必须一样。
注:一般hashCode和equals方法在IDEA同时生成
//向Map中存取,先调hashCode再调用equals
public class HashMapTest {
public static void main(String[] args) {
Student s1 = new Student("coffee");
Student s2 = new Student("coffee");
//重写equals方法之前
// System.out.println(s1.equals(s2)); false
//重写equals方法之后
System.out.println(s1.equals(s2));//true
//没有重写hashCode()方法前,s1和s2的hashCode返回值不一样
System.out.println("s1的hashCode:" + s1.hashCode()); //1163157884
System.out.println("s2的hashCode:" + s2.hashCode()); //1956725890
Set<Student> students = new HashSet<>();
students.add(s1);
students.add(s2);
//按理说,s1和s2一样,两个存入hashSet中,不可重复只会有一个。
//这里如果没有重写hashCoe(),则存入为两个
//这里重写hashCoe(),则存入只有一个
System.out.println(students.size());
}
}
public class Student {
private String name;
public Student() {}
public Student(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
//重写equals方法
public boolean equals(Object o) {
if (o == null || !(o instanceof Student)) return false;
if(o == this) return true;
Student s = (Student) o;
if(this.name == s.name) return true;
return false;
}
//重写hashCode方法
public int hashCode() {
return Objects.hash(name);
}
}
-
放在HashMap集合key部分以及放在hashSet集合中元素,需要同时重写hashCode方法和equals方法。
-
HashMap允许key值或value值为null,但是只能有一个。
public class HashMapTest {
public static void main(String[] args) {
Map map = new HashMap();
map.put(null,null);
System.out.println(map.get(null)); //null
map.put(null,100);
System.out.println(map.get(null)); //100
}
}
3. Hashtable
- Hashtable的key和value都是不能为null的。
public class HashTableTest {
public static void main(String[] args) {
Map map = new Hashtable();
map.put(100,null); //java.lang.NullPointerException
map.put(null,"123"); //java.lang.NullPointerException
}
}
-
Hashtable是线程安全的。效率较低,较少使用。
-
Properties是一个Map集合,继承Hashtable,Properties的key和value都是String。Properties被称为属性对象。
public class PropertiesTest {
public static void main(String[] args) {
//创建一个Properties对象
Properties pro = new Properties();
//Properties存方法
pro.setProperty("url","jdbc:mysql://localhost:3306/ice");
pro.setProperty("driver","com.mysql.jdbc.Driver");
pro.setProperty("username","root");
pro.setProperty("password","123");
//Properties取方法
System.out.println(pro.getProperty("url")); //jdbc:mysql://localhost:3306/ice
System.out.println(pro.getProperty("driver")); //com.mysql.jdbc.Driver
System.out.println(pro.getProperty("username")); //root
System.out.println(pro.getProperty("password")); //123
}
}
4. TreeSet
- TreeSet底层实际上是一个TreeMap,TreeMap集合是一个二叉树。
- 放到TreeSet集合中元素,等同于放到TreeMap集合key部分。
- TreeSet中集合不可重复,但是可以按照元素大小自动排序。
public class TreeSetTest {
public static void main(String[] args) {
//创建一个TreeSet集合
TreeSet<String> ts = new TreeSet<>();
//添加String
ts.add("ice");
ts.add("coffee");
ts.add("is");
ts.add("very");
ts.add("good");
for (String t:
ts) {
System.out.printf(t + " "); //coffee good ice is very
}
System.out.println();
TreeSet<Integer> ts2 = new TreeSet<>();
ts2.add(100);
ts2.add(254);
ts2.add(54);
ts2.add(21);
ts2.add(100);
for (Integer t:
ts2) {
System.out.printf(t + " "); //21 54 100 254
}
}
}
- TreeSet无法对自定义类型进行排序
public class TreeSetTest {
public static void main(String[] args) {
Person p1 = new Person(15);
TreeSet<Person> persons = new TreeSet<>();
persons.add(p1); //Exception in thread "main" java.lang.ClassCastException: collection.Person cannot be cast to java.lang.Comparable
}
}
class Person{
int age;
public Person(int age){
this.age = age;
}
}
-
自定义类型需要排序时,需要实现Comparable接口。Comparable接口中需要实现compareTo()方法。
compareTo()方法:
返回0表示相同,value会覆盖
返回<0,会在左子树上查找
返回>0,会在右子树上查找
public class TreeSetTest {
public static void main(String[] args) {
Cutomer c1 = new Cutomer(32);
Cutomer c2 = new Cutomer(26);
Cutomer c3 = new Cutomer(45);
Cutomer c4 = new Cutomer(23);
TreeSet<Cutomer> cs = new TreeSet<>();
cs.add(c1);
cs.add(c2);
cs.add(c3);
cs.add(c4);
for (Cutomer c:
cs) {
System.out.printf(c.age + " "); //23 26 32 45
}
}
}
//放在TreeSet集合中,需要实现java.lang.Comparable接口
//并实现compareTo方法,equals可以不写
class Cutomer implements Comparable<Cutomer>{
int age;
public Cutomer(int age){
this.age = age;
}
//编写比较逻辑,或者比较规则。
@Override
public int compareTo(Cutomer c) {
return this.age - c.age; //从小到大
//return c.age - this.age; 从大到小
}
}
- TreeSet集合中元素可排序的第二种方式:使用比较器方式。这种方式适用于写多个比较规则。
public class TreeSetTest03 {
public static void main(String[] args) {
//创建TreeSet集合时,需要使用这个比较器
TreeSet<Cutomer> cus = new TreeSet<>(new CutomerComparator());//传一个比较器进去
cus.add(new Cutomer(23));
cus.add(new Cutomer(45));
for (Cutomer c:
cus) {
System.out.printf(c + " "); //Cutomer{age=23} Cutomer{age=45}
}
}
}
class Cutomer{
int age;
public Cutomer(int age){ this.age = age; }
@Override
public String toString() { return "Cutomer{" + "age=" + age + '}'; }
}
//单独编写比较器,比较器实现java.util.Comparator接口
class CutomerComparator implements Comparator<Cutomer> {
@Override
public int compare(Cutomer o1, Cutomer o2) {
//指定比较规则,按照年龄排序
return o1.age - o2.age;
}
}
5.Collections工具类
-
集合接口:java.util.Collection
-
方便集合操作,集合工具类:java.util.Collections
集合工具类常用方法:
将集合转换成线程安全:synchronizedList()
集合排序:sort()
public class CollectionsTest01 {
public static void main(String[] args) {
//ArrayList集合不是线程安全的
List<String> list = new ArrayList<>();
//变成线程安全的
Collections.synchronizedList(list);
//排序
list.add("abf");
list.add("abc");
list.add("abe");
list.add("aba");
Collections.sort(list);
for (String s:
list) {
System.out.printf(s + " "); //aba abc abe abf
}
System.out.println();
//对于自定义类型的排序,需要实现Comparable接口
List<Cutomer2> cus2 = new ArrayList<>();
cus2.add(new Cutomer2(30));
cus2.add(new Cutomer2(20));
cus2.add(new Cutomer2(25));
Collections.sort(cus2);
for (Cutomer2 c:
cus2) {
System.out.printf(c.age + " "); //20 25 30
}
System.out.println();
//Set集合如果想要用sort方法进行排序,需要将Set集合转换成list集合
Set<String> set = new HashSet<>();
set.add("king");
set.add("king2");
set.add("king3");
List<String> mylist = new ArrayList<>(set);
Collections.sort(mylist);
for (String s:
mylist) {
System.out.printf(s + " ");//king king2 king3
}
}
}
class Cutomer2 implements Comparable<Cutomer2>{
int age;
public Cutomer2(int age){
this.age = age;
}
//编写比较逻辑,或者比较规则。
@Override
public int compareTo(Cutomer2 c) {
return this.age - c.age; //从小到大
//return c.age - this.age; 从大到小
}
}
——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频