Java SE 集合:Map接口

目录

  • Java SE 集合:Map接口
    • Values ,HashTable ,CollectionsHashMap 同步
    • 1、Set 接口 hashSet
      • 1.1 构造器
      • 1.2 常用方法(一些)
        • 1.2.1 如何构建存储引用数据类型集合
      • 1.3 遍历
    • 2、Map 接口
      • 2.1、遍历方式:
    • 3、 HashMap
      • 3.1、构造器
      • 3.2、常用方法(一些)
    • 4、TreeMap
    • 5、HashTable
      • 5.2、Properties
        • 5.2.1、构造器
        • 5.2.2、常用方法
    • 6、Collections
    • Values ,HashTable ,CollectionsHashMap 同步

Values ,HashTable ,CollectionsHashMap 同步

1、Set 接口 hashSet

  • 无序,去重

  • HashSet的底层是用 HashMap 实现的

  • 底层结构: 哈希表(数组+链表+红黑树) 不同步

  • 特点:
      做查询,增删效率较高
      无序去重

  • 初始容量: 16
    加载因子: 0.75 ->扩容的临界点

  • 扩容的临界点 = 容量*加载因子;
      12 = 16*0.75; 当存储的数据到12就扩容

注意:

  HashSet 与 TreeSet之间的选择
  如果想要数据存储的时候默认升序|降序->TreeSet
  否则建议选择HashSet

1.1 构造器

构造器 描述
HashSet() 构造一个新的空集; 支持HashMap实例具有默认初始容量(16)和加载因子(0.75)。
HashSet(int initialCapacity) 构造一个新的空集; 支持HashMap实例具有指定的初始容量和默认加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor) 构造一个新的空集; 支持HashMap实例具有指定的初始容量和指定的加载因子。
HashSet(Collection<? extends E> c) 构造一个包含指定集合中元素的新集合。
//HashSet() 构造一个新的空集; 支持HashMap实例具有默认初始容量(16)和加载因子(0.75)。HashSet<String> hs=new HashSet<>();
//HashSet(int initialCapacity) 构造一个新的空集; 支持HashMap实例具有指定的初始容量和默认加载因子(0.75)。HashSet<String> hs2=new HashSet<>(20);

1.2 常用方法(一些)

变量和类型 方法 描述
boolean add(E e) 如果指定的元素尚不存在,则将其添加到此集合中。
void clear() 从该集中删除所有元素。
Object clone() 返回此 HashSet实例的浅表副本:未克隆元素本身。
boolean contains(Object o) 如果此set包含指定的元素,则返回 true 。
boolean isEmpty() 如果此集合不包含任何元素,则返回 true 。
Iterator iterator() 返回此set中元素的迭代器。
boolean remove(Object o) 如果存在,则从该集合中移除指定的元素。
int size() 返回此集合中的元素数(基数)。

1.2.1 如何构建存储引用数据类型集合

构建一个HashSet 存储自定义引用数据类型的数据,做基本操作行为,然后遍历
通过HashSet存储自定义引用数据类型的数据去重问题:
需要在自定义引用数据类型中,重写hashcode与equals方法,根据内容计算比较
hashcode 与 equals :
equals相等hashcode值肯定相等
hashcode相等,equals不一定相等
        HashSet<Person> hash = new HashSet<>();//默认根据地址做去重hash.add(new Person("",18));hash.add(new Person("",18));System.out.println(hash);System.out.println(new Person("",18).hashCode());System.out.println(new Person("",18).hashCode());//HashSet() 构造一个新的空集; 支持HashMap实例具有默认初始容量(16)和加载因子(0.75)。
//HashSet(int initialCapacity) 构造一个新的空集; 支持HashMap实例具有指定的初始容量和默认加载因子(0.75)。
//HashSet(int initialCapacity, float loadFactor) 构造一个新的空集; 支持HashMap实例具有指定的初始容量和指定的加载因子。
//HashSet(Collection<? extends E> c) 构造一个包含指定集合中元素的新集合。HashSet<String> hs=new HashSet<>();hs.add("aja");//boolean add(E e) 如果指定的元素尚不存在,则将其添加到此集合中。hs.add("gkdlg");
//void clear() 从该集中删除所有元素。//Object clone() 返回此 HashSet实例的浅表副本:未克隆元素本身。
//boolean contains(Object o) 如果此set包含指定的元素,则返回 true 。System.out.println(hs.contains("gkdlg"));
//boolean isEmpty() 如果此集合不包含任何元素,则返回 true 。System.out.println(hs.isEmpty());
//Iterator<E> iterator() 返回此set中元素的迭代器。//Iterator iterator= hs.iterator();
//boolean remove(Object o) 如果存在,则从该集合中移除指定的元素。hs.remove("gkdlg");
//int size() 返回此集合中的元素数(基数)。System.out.println( hs.size());
//Spliterator<E> spliterator() 在此集合中的元素上创建late-binding和失败快速 Spliterator 。

1.3 遍历

  //遍历for (String s:hs) {System.out.println(s);}//迭代器Iterator ia= hs.iterator();while(ia.hasNext()){System.out.println(ia.next());}

2、Map 接口

常见的实现类有 HashMapHashTable

  • Map<K,V>
  • 接口
  • 存储键值对的数据
  • k-v 一个映射关系:key->value;一个key只能对应一个value
  • key与value可以为任意类型的一个数据
  • key是唯一的,无序的 --> Set集合
  • value是无序可重复的 --> Collection 无序可重复

注意:如果存入的键值对中key相同,那么后面的会覆盖掉前面的数据存放到Map之后,我们是无

法控制存放数据的顺序的

2.1、遍历方式:

  • Set< K> keySet() 返回所有的key,通过key获取value
  • Collection< V> values() 获取集合中的所有value值,无法获取key
  • Set<Map.Entry<K,V>> entrySet()
             //多态Map<Integer,String> map=new HashMap<>();// 遍历//Set<K> keySet()  返回所有的key,通过key获取valueSystem.out.println("-------- Set<K> keySet()  返回所有的key,通过key获取value-------------");Set<Integer> set=map.keySet();for (int s:set) {System.out.println(s+"-->"+map.get(s));}//Collection<V> values() 获取集合中的所有value值,无法获取keySystem.out.println("-------- Collection<V> values() 获取集合中的所有value值,无法获取key-------------");Collection<String> con=map.values();for (String s:con) {System.out.println(s);}//Set<Map.Entry<K,V>> entrySet()System.out.println("--------Set<Map,Entry<K,V>> entrySet()-------------");Set<Map.Entry<Integer,String>> st=map.entrySet();for (Map.Entry <Integer,String > m:st) {System.out.println(m);}

3、 HashMap

​    HashMap作为Map的实现类,其中的方法都进行了实现,并且定义了可以存储数据的空间,其中使用了

一个叫做 Entry 的内部类,作为数据的存储

  • HashSet --> HashMap 不同步

  • 底层结构:
    哈希表(数组+链表+红黑树)

  • 特点:

    • 存储键值对类型数据
    • 无序,根据key做去重
    • 查询,修改,删除,增加效率较高
  • Hash表存储原理:
    哈希表: Node节点数组,存储节点数据 节点:key,value,hash,next
      1.**put(key,value)**存储键值对类型的数据
    构建一个Node节点对象 new Node(key,value,hash,null)

  • 默认初始容量: 1<<4 16–> 数组长度

  • 加载因子 : 0.75

  • 最大容量… 1<<30

  • 扩容: 新数组的容量为原容量的2倍 newCap = oldCap << 1
    当添加的数据个数>=原数组长度*0.75 的时候,就扩容,扩容的是数组的大小

HashMap存储key如果为自定义引用数据类型:
    key去重的问题 : key类型中要求重写hashcode与equals方法

3.1、构造器

构造器 描述
HashMap() 使用默认初始容量(16)和默认加载因子(0.75)构造一个空 HashMap 。
HashMap(int initialCapacity) 使用指定的初始容量和默认加载因子(0.75)构造一个空 HashMap 。
HashMap(int initialCapacity, float loadFactor) 使用指定的初始容量和加载因子构造一个空 HashMap 。
HashMap(Map<? extends K,? extends V> m) 构造一个新的 HashMap ,其映射与指定的 Map相同。
//HashMap()使用默认初始容量(16)和默认加载因子(0.75)构造一个空 HashMap 。
Map<Integer,String> map=new HashMap<>();
//HashMap(int initialCapacity,  float loadFactor)使用指定的初始容量和加载因子构造一个空 HashMap 。
HashMap<Integer,User> hash = new HashMap<>(20,0.8);

3.2、常用方法(一些)

变量和类型 方法 描述
void clear() 从此映射中删除所有映射。
Object clone() 返回此 HashMap实例的浅表副本:未克隆键和值本身。
V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 尝试计算指定键及其当前映射值的映射(如果没有当前映射, null )。
V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 如果指定的键尚未与值关联(或映射到 null ),则尝试使用给定的映射函数计算其值并将其输入此映射,除非 null 。
V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 如果指定键的值存在且为非null,则尝试在给定键及其当前映射值的情况下计算新映射。
boolean containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。
boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true 。
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图。
V get(Object key) 返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。
boolean isEmpty() 如果此映射不包含键 - 值映射,则返回 true 。
Set keySet() 返回此映射中包含的键的Set视图。
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 如果指定的键尚未与值关联或与null关联,则将其与给定的非空值关联。
V put(K key, V value) 将指定的值与此映射中的指定键相关联。
void putAll(Map<? extends K,? extends V> m) 将指定映射中的所有映射复制到此映射。
V remove(Object key) 从此映射中删除指定键的映射(如果存在)。
int size() 返回此映射中键 - 值映射的数量。
Collection values() 返回此映射中包含的值的Collection视图。

HashMap存储key如果为自定义引用数据类型:
    key去重的问题 : key类型中要求重写hashcode与equals方法

       Map<Integer,String> map=new HashMap<>();//V put(K key, V value) 将指定的值与此映射中的指定键相关联(可选操作)。map.put(1001,"haha");map.put(1010,"zss");map.put(1005,"hjfnks");map.put(1002,"vghdjkgv");map.put(1004,"sfskl");map.put(1012,"dsjkv");System.out.println(map);// 遍历//Set<K> keySet()  返回所有的key,通过key获取valueSystem.out.println("-------- Set<K> keySet()  返回所有的key,通过key获取value-------------");Set<Integer> set=map.keySet();for (int s:set) {System.out.println(s+"-->"+map.get(s));}//Collection<V> values() 获取集合中的所有value值,无法获取keySystem.out.println("-------- Collection<V> values() 获取集合中的所有value值,无法获取key-------------");Collection<String> con=map.values();for (String s:con) {System.out.println(s);}//Set<Map.Entry<K,V>> entrySet()System.out.println("--------Set<Map,Entry<K,V>> entrySet()-------------");Set<Map.Entry<Integer,String>> st=map.entrySet();for (Map.Entry <Integer,String > m:st) {System.out.println(m);}System.out.println("==================================");//V get(Object key)  根据key获取value,没有返回nullSystem.out.println(map.get(1005));//int size()System.out.println(map.size());//boolean containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。//boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true 。System.out.println(map.containsKey(1012)); //trueSystem.out.println(map.containsValue("zss"));//true//V remove(Object key) 如果存在,则从该映射中移除键的映射(可选操作)。System.out.println(map.remove(1021));// nulls//default V replace(K key, V value) 仅当指定键当前映射到某个值时,才替换该条目的条目。System.out.println(map.remove(1021,"hahd"));//flase//default boolean replace(K key, V oldValue, V newValue) 仅当前映射到指定值时,才替换指定键的条目。System.out.println(map.replace(1001,"haha","heihei"));//true

4、TreeMap

  • extends AbstractMap<K,V>
  • TreeSet底层就是由TreeMap维护的
  • 底层结构: 红黑树
  • 特点: 有序(默认升序排序),存放存储与内部真实存储顺序是不一致的
  • 此实现不同步
  • 注意: 以后什么功能|结构能够实现排序的,想到比较器:指定比较规则

去重排序: 根据key实现去重与排序
  key中存放的数据的类型->1)实现内部比较器 2)实现外部比较器

TreeMap<Student,String> trst=new TreeMap<>();存储数据,在存储的时候才会比较,排序
trst.put(new Student(1020,"haha",20,'男'),"大数据");
trst.put(new Student(1002,"dudu",22,'男'),"java");
trst.put(new Student(1001,"cici",21,'男'),"大数据");
trst.put(new Student(1014,"bibi",24,'男'),"html");map.put(null,null);  //treemap中存储键值对key不能为null
System.out.println(trst);//外部比较器
TreeMap<Student,String> trst2=new TreeMap<>((o1, o2) -> o1.getName().compareTo(o2.getName()));trst2.putAll(trst);System.out.println(trst2);

5、HashTable

  • 实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键或值。
  • 初始容量负载因子容量是哈希表中的数, 初始容量只是创建哈希表时的容量。
    • 默认初始容量(11)和加载因子(0.75)
  • extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable
构造器 描述
Hashtable() 使用默认初始容量(11)和加载因子(0.75)构造一个新的空哈希表。
Hashtable(int initialCapacity, float loadFactor) 使用指定的初始容量和指定的加载因子构造一个新的空哈希表。
变量和类型 方法 描述
void clear() 清除此哈希表,使其不包含任何键。
V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) 尝试计算指定键及其当前映射值的映射(如果没有当前映射, null )。
boolean contains(Object value) 测试某些键是否映射到此哈希表中的指定值。
boolean containsKey(Object key) 测试指定的对象是否是此哈希表中的键。
boolean containsValue(Object value) 如果此哈希表将一个或多个键映射到此值,则返回true。
Enumeration elements() 返回此哈希表中值的枚举。
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图。
boolean equals(Object o) 根据Map接口中的定义,将指定的Object与此Map进行相等性比较。
V get(Object key) 返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。
int hashCode() 根据Map接口中的定义返回此Map的哈希码值。
boolean isEmpty() 测试此哈希表是否将键映射到值。
Enumeration keys() 返回此哈希表中键的枚举。
Set keySet() 返回此映射中包含的键的Set视图。
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) 如果指定的键尚未与值关联或与null关联,则将其与给定的非空值关联。
V put(K key, V value) 将指定的 key映射到此哈希表中的指定 value 。
void putAll(Map<? extends K,? extends V> t) 将指定映射中的所有映射复制到此哈希表。
protected void rehash() 增加此哈希表的容量并在内部重新组织,以便更有效地容纳和访问其条目。
V remove(Object key) 从此哈希表中删除键(及其对应的值)。
int size() 返回此哈希表中的键数。
String toString() 以一组条目的形式返回此 Hashtable对象的字符串表示形式,用大括号括起,并用ASCII字符“ , ”(逗号和空格)分隔。
Collection values() 返回此映射中包含的值的Collection视图。

5.2、Properties

    Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。

  • 存储的键值对都为字符串类型
  • Properties类表示一组持久的属性。
  • extends Hashtable<Object,Object>
  • Properties可以保存到流中或从流中加载。

总结:

  • properties作为配置文件使用的步骤:

    • 1.src下新建一个file文件,名字问xx.properties
    • 2.zai配置文件中定义键值对为字符串的数据(注意不加"",;…)
    • 3.程序中定义个Properties对象.通过对象.load(流) 指定从某一个资源文件中读取数据,使用指定的流
    • 4.对象.getProperty(key)
  • properties根式作为配置文件的格式存在,键值对 都是字符串,简单

  • 程序从properties文件中读取数据:
    void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。

5.2.1、构造器

构造器 描述
Properties() 创建一个没有默认值的空属性列表。
Properties(int initialCapacity) 创建一个没有默认值的空属性列表,并且初始大小容纳指定数量的元素,而无需动态调整大小。
Properties(Properties defaults) 创建具有指定默认值的空属性列表。

5.2.2、常用方法

变量和类型 方法 描述
String getProperty(String key) 在此属性列表中搜索具有指定键的属性。
String getProperty(String key, String defaultValue) 在此属性列表中搜索具有指定键的属性。
void list(PrintWriter out) 将此属性列表打印到指定的输出流。
void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
         Properties ppt=new Properties();//新增方法ppt.setProperty("123","456");//通过properties对象从配置文件中读取数据//指定从哪一个文件,通过哪个流加载ppt.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("week04/day01/properties02/hhh.properties"));System.out.println(ppt.getProperty("name1"));System.out.println(ppt.getProperty("sex1"));System.out.println(ppt.getProperty("name2"));System.out.println(ppt.getProperty("sex2"));System.out.println(ppt.getProperty("嘿嘿"));

6、Collections

Collections 与 Collection 之间的区别:

  • Collection 集合体系的上层接口
  • Collections 操作集合数据的工具类 , 与 Arrays很像

常用方法:

  • void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。

  • void shuffle(List) //对List容器内的元素进行随机排列

  • void reverse(List) //对List容器内的元素进行逆续排列

  • void fill(List, Object) //用一个特定的对象重写整个List容器

  • int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
    使用前提: 升序排序

HashMap线程不安全问题:

  • ​ 1.Hashtable类型,线程安全类型的Map

    • Hashtable是同步的。 如果不需要线程安全实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的高度并发实现,则建议使用ConcurrentHashMap代替Hashtable 。
  • 2.Collections -> static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
  • 3.juc包(高级并发编程包) - > ConcurrentHashMap 线程安全的哈希表—>推荐,安全性好效率高
        List<Integer> lt=new ArrayList<>();lt.add(23);lt.add(34);lt.add(56);lt.add(86);lt.add(1);System.out.println(lt);//void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。Collections.sort(lt);System.out.println(lt);
//          void shuffle(List) //对List容器内的元素进行随机排列Collections.shuffle(lt);System.out.println(lt);
//        void reverse(List) //对List容器内的元素进行逆续排列Collections.reverse(lt);System.out.println(lt);
//        void fill(List, Object) //用一个特定的对象重写整个List容器Collections.fill(lt,56);System.out.println(lt);
//        int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
//            使用前提: 升序排序Collections.sort(lt);Collections.binarySearch(lt,34);System.out.println(lt);

Values ,HashTable ,CollectionsHashMap 同步

Java SE 集合:Map接口相关推荐

  1. 9.Java SE 集合

    9.Java SE 集合 集合 Collection List 接口 Set 接口 Map HashMap TreeMap Properties Collection 接口 面试题 List 接口 面 ...

  2. (7)Java数据结构--集合map,set,list详解

    MAP,SET,LIST,等JAVA中集合解析(了解) - clam_clam的专栏 - CSDN博---有颜色, http://blog.csdn.net/clam_clam/article/det ...

  3. Java集合类之Map接口之学生花名册

    Java集合类之Map接口之学生花名册 任务描述 把给定的学生花名册数据添加到 Map 集合中. 相关知识 在 Java 的集合体系中,主要包含 Collection 接口以及 Map 接口,将介绍 ...

  4. 2021-06-19复习java Collection集合 Iterator接口_迭代器 增强for循环 泛型

    2021-06-19复习java Collection集合 Iterator接口_迭代器 增强for循环 泛型 Collection集合 java.util.coLlection接口 所有单列集合的最 ...

  5. Java从零开始学二十三(集合Map接口)

    一.Map接口 Collection.Set.List接口都属于单值的操作,即:每次只能操作一个对象,而Map与它们不同的是,每次操作的是一对对象,即二元偶对象,Map中的每个元素都使用key à v ...

  6. java hashedmap_Java基础 - Map接口的实现类 : HashedMap / LinkedHashMap /TreeMap 的构造/修改/遍历/ 集合视图方法/双向迭代输出...

    import java.util.*; /**一:Collection接口的 * Map接口: HashMap(主要实现类) : HashedMap / LinkedHashMap /TreeMap ...

  7. Java基础—集合2Set接口和Map接口

    第一讲 Set 一 概述 Set:1. 元素存储无下标,所以元素是无序(存入和取出的顺序不一定一致) 2. 元素不可以重复 |--HashSet:底层数据结构是哈希表.线程不同步. 保证元素唯一性的原 ...

  8. Java集合——Map接口学习总结

    一.HashMap实现类 1.常用方法 增加:put(K key, V value)删除:clear() remove(Object key)修改:查看:entrySet() get(Object k ...

  9. Java学习(Map接口)

    一.概述: 我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图. 1. Collection中的集合,元素是孤立存在的(理解为单身), ...

  10. java comparator_Java 集合排序策略接口 Comparator

    1. 前言 最近用到了集合排序(基于 Java 8).现在我能用 Stream 的就用 Stream ,真香!排序可以这么写: List peoples = new ArrayList<> ...

最新文章

  1. 云炬创业政策学习笔记20210111
  2. Java黑皮书课后题第10章:*10.20(近似e)编程练习题5.26使用下面数列近似计算e(略),为了得到更好的精度,在计算中使用25位精度的BigDecimal
  3. 线程间的通信 设置线程等待与线程唤醒
  4. 解决为什么导入了tomcat进入myeclipse却在server中找不到
  5. Lodop输出页面input文本框的最新值
  6. 【AI 顶会】NIPS2019接收论文完整列表
  7. Python中出现“TabError: inconsistent use of tabs and spaces in indentation”
  8. struts的比较标签,在使用时,与测试时需要留心。
  9. 服务器2008r2网络禁止修改,windows-server-2008-r2 – Windows 2008 R2标准服务器 – 如何禁用RC4...
  10. jQuery EasyUI 使用笔记
  11. 阿里云毕龙飞:五个维度推进企业生产关系数字化
  12. HDU4825/5536 [01 字典树/简单字典树更新]
  13. 2020-8-6 Codeforces摸鱼报告
  14. VSCode调试JavaScript代码方法
  15. 简道云-第5章-流程
  16. pnpm安装使用教程以及pnpm node版本管理以及EPERM operation not permitted symlink问题解决
  17. 2021年安全生产模拟考试(全国特种作业操作证电工作业-电气试验模拟考试题库二)安考星
  18. 不断收集一些不错的博客(献给未来路上的人)
  19. ArcGIS:如何利用栅格数据进行路径网络分析-可达性分析?
  20. 传奇3服务器.gen文件,传奇3刺客4职业gsp国际版+教程+客户端

热门文章

  1. Latex表格内容自动换行
  2. layui表格中显示内容换行
  3. 全方位剖析“清华同方”,脉络千里!!
  4. STM32利用DCMI对并口ADC高速数据采样
  5. 基本办公软件的使用及技巧:便签篇
  6. XtraBackUp 全量备份
  7. matlab 对数回归,对数拟合
  8. qt creator报错 error: C1083: 无法打开包括文件:“QMediaPlayer”
  9. 新浪微博登录html代码,新浪微博登录仿DiscuzX1.5论坛QQ登录样式 - YangJunwei
  10. FIN断开-TCP segment of a reassembled PDU-原因分析