集合

集合是Java API所提供的一系列类,可以用于动态存放多个对象 (集合只能存对象)

集合类全部支持泛型,是一种数据安全的用法。

集合和数组的区别:

  1. 长度区别:数组固定、集合可变
  2. 内容区别:
    • 数组可以是基本类型,也可以是引用类型
    • 集合只能是引用类型
  3. 元素内容:
    • 数组只能存储同一种类型
    • 集合可以存储不同类型(集合一般也只存储同一种类型)

集合框架结构图


Map

Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value。Map 提供 key 到 value 的映射,你可以通过“键”查找“值”。

  1. HashMap 接口实现类 ,没有同步, 线程不安全

    • LinkedHashMap 双向链表和哈希表实现
  2. Hashtable 接口实现类,同步,线程安全
    • Properties
  3. TreeMap 接口实现类,红黑树对所有的key进行排序
  4. ConcurrentHashMap

Map集合的方法

方法 描述
void clear() 移除该Map中的所有映射(清空)
boolean containsKey(Object key) 查询Map中是否包含指定key,如果包含则返回true
boolean containsValue(Object value) 查询Map中是否包含指定value,如果包含则返回true
V put(K key, V value) 将指定value与此映射中的指定key相关联。如果已经存在key,则旧value将被替换。(可用于添加、替换)
void putAll(Map m) 将所有映射从指定映射m复制到此映射
V get(Object key) 返回指定key所对应的value,如Map中不包含key则返回null
V getOrDefault(Object key, V defaultValue) 通过key获取value,如果key不存在,返回默认值
boolean isEmpty() 查询Map是否为空,如果空则返回true
V putIfAbsent(K key,V value) 如果指定的key没有对应的value(或value为null )将其与给定的value关联并返回null ,否则返回当前value。
V remove(Object key) 如果key存在,则删除指定key的映射。并返回与key关联的value,如果key 不存在,则返回null
boolean remove(Object key,Object value) 仅当key与value匹配时,才删除指定key的条目,并返回true
Collection<V> values() 返回该Map里所有value组成的Collection
Set<K> keySet() 返回该Map中所有key所组成的set集合
Set<Map.Entry<K,V>> entrySet() 返回Map中所包含的键值对所组成的Set集合,每个集合元素都是Map.Entry对象(Entry是Map的内部类)。
int size() 返回该Map里的键值对的个数。

HashMap

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。

  • HashMap继承自AbstractMap类,实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,不支持线程同步。(如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap)

    public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {
  • HashMap最多只有一个key值为null,但可以有无数多个value值为null。

  • HashMap 是无序的,即不会记录插入的顺序。

  • HashMap底层数组长度必须为2的幂,这样做是为了hash准备,默认为16。

//创建HashMap对象
HashMap<String, Integer> map = new HashMap<>();
//添加
map.put("樱泽墨", 17);
map.put("更科瑠夏", 19);
map.put("一之濑千鹤", 19);
//将指定值与此映射中的指定键相关联。如果映射先前包含键的映射,则旧值将被替换。
//替换(返回原来的value) 返回与key关联的旧值,如果没有key的映射,则为null
Integer put = map.put("樱泽墨", 16);
System.out.println("返回与key关联的旧值:" + put);//17
//替换
map.replace("更科瑠夏", 17);//通过key替换value
map.replace("一之濑千鹤", 19, 18);//通过key+value替换value
//获取(通过key获取value)
Integer integer = map.get("樱泽墨");//返回指定Key映射到的值,如果此映射不包含Key的映射,则返回null 。
System.out.println("通过key获取value:" + integer);//16
//获取(通过key获取value,如果key不存在,返回默认值)
Integer orDefault = map.getOrDefault("哈尔卡拉", 666);
System.out.println("通过key获取value,如果key不存在,返回默认:" + orDefault);//666
System.out.println("判断此集合中是否包含某个Key:" + map.containsKey("樱泽墨"));//true
System.out.println("判断此集合中是否包含某个Value:" + map.containsValue(18));//true
System.out.println("判断此集合中是否没有元素:" + map.isEmpty());//true-没有元素 false-有元素
//将新集合中所有的元素添加到现有集合中
HashMap<String, Integer> newMap = new HashMap<>();
newMap.put("a", 10);
newMap.put("bb", 20);
map.putAll(newMap);
System.out.println(map);//{樱泽墨=16, bb=20, a=10, 更科瑠夏=17, 一之濑千鹤=18}
//添加
//如果集合中有key,就返回value,不替换原来的值
//如果集合中没有key,就返回null并添加
Integer putIfAbsent = map.putIfAbsent("中野三玖", 17);
System.out.println(putIfAbsent);//null
System.out.println(map);//{樱泽墨=16, bb=20, a=10, 更科瑠夏=17, 中野三玖=17, 一之濑千鹤=18}
//删除
map.remove("bb");//通过key删除映射关系
map.remove("a", 10);//通过key+value删除映射关系
System.out.println("获取映射关系的个数:" + map.size());//7
System.out.println(map);//{樱泽墨=16, 更科瑠夏=17, 中野三玖=17, 一之濑千鹤=18}
//获取所有的value值
Collection<Integer> values = map.values();
//集合-->数组-->字符串
System.out.println(Arrays.toString(values.toArray()));//[16, 17, 17, 18]
//keySet()遍历:将map集合中所有的key获取出,存放在Set集合中,遍历Set依次获取出key,就能获取到对应value
Set<String> keySet = map.keySet();
for (String key : keySet) {Integer value = map.get(key);//返回指定Key映射到的值System.out.println(key + " -- " + value);
}
//樱泽墨 -- 16
//更科瑠夏 -- 17
//中野三玖 -- 17
//一之濑千鹤 -- 18
//entrySet()遍历:将map集合中所有的映射关系(Entry)获取出,存放在Set集合中,遍历Set依次获取出Entry
Set<Entry<String, Integer>> set = map.entrySet();
for (Entry<String, Integer> entry : set) {String key = entry.getKey();Integer value = entry.getValue();System.out.println(key + " -- " + value);
}
//清空集合
map.clear();
System.out.println(map);// {}

LinkedHashMap

LinkedHashMap = HashMap + 双向链表

LinkedHashMap继承自HashMap,所以LinkedHashMap拥有HashMap的所有特性。

LinkedHashMap使用双向链表来维护键值对的次序,迭代顺序与键值对的插入顺序保持一致,性能低于HashMap

public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>
{...}

Hashtable

HashTable是较为远古的使用Hash算法的容器结构,现在基本已被淘汰,单线程转为使用HashMap,多线程使用ConcurrentHashMap。

  • Hashtable与 HashMap类似,继承自Dictionary类,不允许记录的键或者值为null

  • 很多方法都是用synchronized修饰,支持线程同步( 线程安全 ),即任一时刻只有一个线程能写Hashtable,导致了 Hashtable在写入时会比较慢

  • HashTable底层数组长度可以为任意值,造成了hash算法散射不均匀,容易造成hash冲突,默认为11

public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {...}

Properties

Properties 类是Java 语言的配置文件所使用的类。 Xxx.properties 为Java 语言常见的配置文件,以key=value 的 键值对的形式进行存储值,key值不能重复

  • 继承了Hashtable 类,以Map 的形式进行放置值,put(key,value)、get(key)
public class Properties extends Hashtable<Object,Object> {...}

Properties的使用

src/DBConfig.properties

username = Miku
password = 12345

Test.java

public class Test {public static void main(String[] args) throws IOException {//创建Properties的对象Properties p = new Properties();//把配置文件加载到p对象中p.load(Test01.class.getClassLoader().getResourceAsStream("DBConfig.properties"));//load()从输入字节流中读取属性列表(键值对)//getClassLoader()返回此对象表示的类或接口的类加载器//getResourceAsStream()用于读取资源的输入流,如果找不到资源,则为null//获取配置文件中数据String username = p.getProperty("username");//getProperty()在此属性列表中搜索具有指定键的属性。如果未找到将返回nullString password =  p.getProperty("password");System.out.println(username + " -- " + password);//Miku -- 12345}
}

TreeMap

TreeMap实现了红黑树的结构,形成了一颗二叉树。能够把保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器。

  • TreeMap继承于AbstractMap,实现了Map, Cloneable, NavigableMap, Serializable接口。

    public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable {...}
    

特点:

  • 不允许出现重复的key;
  • key不可以为null(若要允许空值可通过自定义比较器来创建TreeMap),value可以为null;
  • 可以对元素进行排序;
  • 无序集合(插入和遍历顺序不一致);

TreeMap特殊方法

方法 描述
Map.Entry firstEntry() 返回最小key所对应的键值对,如Map为空,则返回null
Map.Entry lastEntry() 返回最大key所对应的键值对,如Map为空,则返回null
Object firstKey() 返回最小key,如果为空,则返回null
Object lastKey() 返回最大key,如果为空,则返回null
Map.Entry higherEntry(0bject key) 返回位于key后一位的键值对,如果为空,则返回null
Map.Entry lowerEntry(0bject key) 返回位于key前一位的键值对,如果为空,则返回null
Object lowerKey (object key) 返回位于key前一位key值,如果为空,则返回null
TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("c", 21);
treeMap.put("f", 10);
treeMap.put("a", 28);
treeMap.put("d", 8);
treeMap.put("b", 21);
Set<Entry<String, Integer>> entrySet = treeMap.entrySet();
for (Entry<String, Integer> entry : entrySet) {String key = entry.getKey();Integer value = entry.getValue();System.out.println(key + " -- " + value);
}
//a -- 28
//b -- 21
//c -- 21
//d -- 8
//f -- 10
Entry<String, Integer> firstEntry = treeMap.firstEntry();
System.out.println("返回最小key所对应的键值对:" + firstEntry);//a=28
Entry<String, Integer> lastEntry = treeMap.lastEntry();
System.out.println("返回最大key所对应的键值对:" + lastEntry);//f=10
System.out.println("返回最小key:" + treeMap.firstKey());//a
System.out.println("返回最大key:" + treeMap.lastKey());//f
Entry<String, Integer> higherEntry = treeMap.higherEntry("b");
System.out.println("返回位于b后一位的键值对:" + higherEntry);//c=21
Entry<String, Integer> lowerEntry = treeMap.lowerEntry("c");
System.out.println("返回位于c前一位的键值对:" + lowerEntry);//b=21

TeeMap使用内置排序接口

需求:学生对象存在TreeMap key的位置,用年龄排序

Student.java

//实现Comparable接口
public class Student implements Comparable<Student>{private String name;private int age;public Student() {}public Student(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;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + "]";}//重写compareTo()方法@Overridepublic int compareTo(Student o) {return this.age-o.age;}
}

Test.java

TreeMap<Student, String> map = new TreeMap<>();
map.put(new Student("中野三玖", 17), "12138");
map.put(new Student("中野二乃", 18), "12137");
map.put(new Student("中野四叶", 16), "12139");
Set<Entry<Student, String>> entrySet = map.entrySet();
for (Entry<Student, String> entry : entrySet) {Student key = entry.getKey();String value = entry.getValue();System.out.println(key + " -- " + value);
}
//Student [name=中野四叶, age=16] -- 12139
//Student [name=中野三玖, age=17] -- 12138
//Student [name=中野二乃, age=18] -- 12137

TreeMap使用外置排序接口

需求:学生对象存在TreeMap key的位置,先按照名字长度排序,长度一致按照年龄排序

TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {if (o1.getName().equals(o2.getName()) && o1.getAge() == o2.getAge()) {return 0;}if (o1.getName().length() != o2.getName().length()) {return o1.getName().length() - o2.getName().length();}if (o1.getAge() != o2.getAge()) {return o1.getAge() - o2.getAge();}return 1;}
});
map.put(new Student("椎名真白", 18), "12137");
map.put(new Student("更科瑠夏", 17), "12138");
map.put(new Student("樱泽墨", 16), "12139");
Set<Entry<Student, String>> entrySet = map.entrySet();
for (Entry<Student, String> entry : entrySet) {Student key = entry.getKey();String value = entry.getValue();System.out.println(key + " -- " + value);
}
//Student [name=樱泽墨, age=16] -- 12139
//Student [name=更科瑠夏, age=17] -- 12138
//Student [name=椎名真白, age=18] -- 12137

ConcurrentHashMap

ConcurrentHashMap是HashMap的一个线程安全的、支持高效并发的版本。

  • ConcurrentHashMap不同于HashMap,它既不允许key值为null,也不允许value值为null。
  • 在默认理想状态下,ConcurrentHashMap可以支持16个线程执行并发写操作及任意数量线程的读操作。
  • 在ConcurrentHashMap中,线程对映射表做读操作时,一般情况下不需要加锁就可以完成,对容器做结构性修改的操作(比如,put操作、remove操作等)才需要加锁。
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>implements ConcurrentMap<K,V>, Serializable {...}

高效并发机制是通过以下保证的

  • 通过锁分段技术保证并发环境下的写操作;
  • 通过 HashEntry的不变性、Volatile变量的内存可见性和加锁重读机制保证高效、安全的读操作;
  • 通过不加锁和加锁两种方案控制跨段操作的的安全性。

ConcurrentHashMap读操作不需要加锁在于以下三点:

  • 用HashEntery对象的不变性来降低读操作对加锁的需求;
  • 用Volatile变量协调读写线程间的内存可见性;
  • 若读时发生指令重排序现象,则加锁重读;

ConcurrentHashMap 采用了分段锁技术,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。

注意

  1. Collection 与 Map的区别

    Collection 存单个值,可以获取迭代器进行遍历
    Map存两个值(Key-Value),不可以获取迭代器,不能遍历(Map可以间接遍历)

  2. 理解Set为什么是无序

    无序:存入顺序和取出顺序不一致,无序不等于随机

  3. ArrayList 与 LinkedList的区别

    使用上的区别:

    LinkedList添加了
    队列模式-先进先出(removeFirst())
    栈模式-先进后出(removeLast())

    效率上的区别:由于ArrayList底层数据结构是一维数组,LinkedList底层数据结构是双向链表

    添加 - 不扩容的情况:ArrayList快
    添加 - 扩容的情况:LinkedList快
    注意: 工作中常用ArrayList,因为很多需求都需要使用查询功能,ArrayList查询更快

集合总结

数据结构 是否有序 线程安全 能否存null
ArrayList 数组 有序 不安全
LinkedList 链表 有序 不安全
Vector 数组 有序 安全
HashSet 哈希表 无序且唯一 不安全
LinkedHashSet 链表+哈希表 有序且唯一 不安全
TreeSet 二叉树(红黑树) 自动排序且唯一 不安全 不能
HashMap 哈希表 无序且key唯一 不安全 key最多一个为null
value可以多个为null
LinkedHashMap 链表+哈希表 有序且key唯一 不安全 key最多一个为null
value可以多个为null
Hashtable 哈希表 无序且key唯一 安全,效率低 不能
ConcurrentHashMap 哈希表 无序且key唯一 安全,效率高 不能
TreeMap 二叉树(红黑树) 对Key排序且key唯一 不安全 key不能为null
(可通过自定义比较器)

ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

JavaEE - 集合 - Map集合相关推荐

  1. 【Groovy】map 集合 ( map 集合操作符重载 | 使用 << 操作符添加一个元素 | 代码示例 )

    文章目录 一.使用 " << " 操作符添加一个元素 二.代码示例 一.使用 " << " 操作符添加一个元素 对 map 集合 使用 ...

  2. 【Groovy】map 集合 ( map 集合操作符重载 | + 操作符重载 | 代码示例 )

    文章目录 一.map 集合 " + " 操作符重载 二.代码示例 一.map 集合 " + " 操作符重载 对 map 集合使用 " + " ...

  3. 【Groovy】map 集合 ( map 集合遍历 | 使用 map 集合的 each 方法遍历 map 集合 | 代码示例 )

    文章目录 一.使用 map 集合的 each 方法遍历 map 集合 二.代码示例 一.使用 map 集合的 each 方法遍历 map 集合 遍历 map 集合 , 可以调用 map 集合的 eac ...

  4. 常见的数据结构:栈 队列 数组 链表 红黑树——List集合 _ HashSet集合、可变参数 collections集合 Map集合

    2021-06-07复习java 一.常见的数据结构 栈(先进后出) 队列 数组 链表 红黑树 二.List集合_介绍&常用方法 ArrayList集合 Linkedlist集合 三.Hash ...

  5. 【Groovy】map 集合 ( map 集合操作符重载 | *. 展开操作符 | 代码示例 )

    文章目录 一.map 集合 " *. " 展开操作符 二.代码示例 一.map 集合 " *. " 展开操作符 对 map 集合使用 " *. &qu ...

  6. 【Groovy】map 集合 ( map 集合操作符重载 | - 操作符重载 | 代码示例 )

    文章目录 一.map 集合 " - " 操作符重载 二.完整代码示例 一.map 集合 " - " 操作符重载 对 map 集合 使用 " - &qu ...

  7. 【Groovy】map 集合 ( map 集合遍历 | 使用 map 集合的 find 方法遍历 map 集合 | 代码示例 )

    文章目录 一.使用 map 集合的 find 方法遍历 map 集合 二.代码示例 一.使用 map 集合的 find 方法遍历 map 集合 使用 map 集合的 find 方法遍历 map 集合 ...

  8. 【Groovy】map 集合 ( map 集合定义 | 通过 getClass 函数获取 map 集合的类型 | 代码示例 )

    文章目录 一.map 集合定义 二.获取 map 集合类型 三.代码示例 一.map 集合定义 声明键值对 , 其中 键 Key 可以 不使用引号 , 可以 使用单引号 '' , 也可以 使用双引号 ...

  9. Java——集合(Map集合的两种迭代)

    一,Map集合的第一种迭代 Map集合的第一种迭代,通过get(key)方法,根据键去获取值 package com.wsq.map;import java.util.HashMap; import ...

最新文章

  1. struts2漏洞监测_Apache Shiro身份验证绕过漏洞风险提示
  2. 一个电脑白痴与黑客的对话
  3. leetcode 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭
  4. volatile的作用(转自于http://www.csdn.net/)——今天差点忘记了!
  5. 简单搞定linux逻辑卷
  6. JZOJ 3418. 【NOIP动态规划专题】选课
  7. python--json pickle 模块
  8. 一篇文章带你领悟 Frida 的精髓(基于安卓8.1)
  9. ROS中阶笔记(七):机器人SLAM与自主导航—SLAM功能包的使用
  10. 索尼入局电动汽车市场 宣布成立移动出行公司
  11. 导出WPS office文档格式的说明
  12. 2017年校园招聘中国银行、中国邮政储蓄银行、中国移动笔试内容
  13. 虚妄的奇迹,血泪的现实——记Fateamp;n…
  14. PS——字体斜阴影效果
  15. 什么是独立主机?独立主机的优势有些?
  16. 正北坐标系和车辆坐标系下的heading转换
  17. 【CSS】背景样式(颜色、图片、平铺、附着和位置)
  18. 主线程 如何控制 子线程
  19. 苹果开发者帐号关联大批量下架如何规避?
  20. Hibernate-基础学习

热门文章

  1. 【室内定位】常用的机器人定位导航技术及优缺点
  2. 蓝鲸智云5.1版本安装部署(完整版)
  3. 红帽Rhel7.6操作系统更换成Centos7.6的操作系统
  4. PyCharm社区版安装教程和环境配置及使用
  5. 【MySQL进阶】存储过程及存储函数
  6. CRMEB-知识付费系统程序配置之直播配置(方法二下)
  7. 图解 git 仓库概念
  8. 连锁酒店网络互联解决方案
  9. dell 服务器面板显示屏,Dell服务器面板错误码详解
  10. python dataframe如何设置并处理(删除、填充)空值