精华内容
下载资源
问答
  • 用户定义的类阵列列表示例import java.util.*;public class UserDefinedClassInArrayList {public static void main(String[] args) {//Creating user defined class objectsStudent s1=new Student(1,"AAA",13);...

    用户定义的类阵列列表示例

    import java.util.*;

    public class UserDefinedClassInArrayList {

    public static void main(String[] args) {

    //Creating user defined class objects

    Student s1=new Student(1,"AAA",13);

    Student s2=new Student(2,"BBB",14);

    Student s3=new Student(3,"CCC",15);

    ArrayList al=new ArrayList();

    al.add(s1);

    al.add(s2);

    al.add(s3);

    Iterator itr=al.iterator();

    //traverse elements of ArrayList object

    while(itr.hasNext()){

    Student st=(Student)itr.next();

    System.out.println(st.rollno+" "+st.name+" "+st.age);

    }

    }

    }

    class Student{

    int rollno;

    String name;

    int age;

    Student(int rollno,String name,int age){

    this.rollno=rollno;

    this.name=name;

    this.age=age;

    }

    }

    节目输出:

    1 AAA 13

    2 BBB 14

    3 CCC 15

    展开全文
  • String strs = {"a", "b", "c"}但是,如果我们创建List集合,或者Map集合时,也想快速地为它赋初始值,应当如何做呢?解决方式方式1:调用函数请看如下代码:ArrayList list = new ArrayList<>(Arrays.as...

    问题简介

    在Java当中,若希望在创建数组的同时给数组赋值很简单,可以想下面这样:

    int[] num = {1,2,3};

    String strs = {"a", "b", "c"}

    但是,如果我们创建List集合,或者Map集合时,也想快速地为它赋初始值,应当如何做呢?

    解决方式

    方式1:调用函数

    请看如下代码:

    ArrayList list = new ArrayList<>(Arrays.asList("aa", "bb", "cc"));

      Arrays.asList(T... a) 方法的参数是一个可变长参数,也就是说他能够传入一个数组,也能够传入多个参数,而它的作用就是将传入的数组或多个数据封装成List集合返回,而上面的代码就是接收返回的List集合,并将其作为参数传入ArrayList的构造方法,创建一个新的ArrayList对象。

    说到这里有人可能要问了,为什么不能直接将asList方法的返回值赋给list参数,而要将它传入构造器创建新的对象呢?这不是脱裤子放屁——多此一举吗。当然不是,请看下面的代码:

    // 代码1

    List list1 = Arrays.asList("aa", "bb", "cc");

    list1.add("dd"); // UnsupportedOperationException

    // 代码2

    String[] str = {"a","b","c"};

    List list = Arrays.asList(str);

    str[0] = "e"; // list中的0号位置也一同改变

      上面有两段代码,看似没有问题,但是运行结果却和大家想象的有些不同。首先代码1,使用asList方法返回的创建的List对象,不允许进行修改操作,否则将会抛出一个UnsupportedOperationException;再来看代码2,我们将一个数组作为asList的参数,得到一个List对象,但是此时我们改变这个数组中元素的值,list对象的值也会发生改变,因为这个List对象底层引用的就是这个数组,并且和代码1一样,这个list也不能修改。

    但是,若我们将返回的List对象作为参数传入ArrayList的构造器中,这个问题就不会发生,因为ArrayList的构造器将会把传入的list中所有的元素复制一份,因此不会影响到原数组,且可以随意改变。

    方式2:匿名内部类

    这是一个非常机智的方式,就是看到了下面这行代码,我才忍不住写了这篇博客:

    List list = new ArrayList(){ {add("a"); add("b"); add("c");} };

    乍一看是不是有点懵逼,我们将这段代码展开来看,就会清晰很多:

    List list = new ArrayList() {

    {

    add("a");

    add("b");

    add("c");

    }

    };

    这下应该比之前容易理解了。这段代码就是创建了一个匿名内部类对象,且这个类继承自ArrayList,在这个匿名内部类中添加了一个非静态代码块,并在代码块中调用了三次add方法,为这个List对象赋值。

    我们知道,若我们想创建一个对象,可以直接new 构造方法,但是我们若想写一个匿名内部类,这个匿名内部类继承自某个类,只需在构造方法后面加上一对大括号。同时,非静态代码块会在构造方法执行前被执行,所以我们将赋值语句放在了代码块中,于是就有了上面这段代码。若还是看不明白,没关系,看下面这段代码十有八九就明白了,我们将上面的代码换另一种方式写出来:

    public class Test {

    public static void main(String[] args) {

    List list = new MyList();

    }

    }

    // 创建一个类继承自ArrayList

    class MyList extends ArrayList{

    // 在类的非静态代码块中编写赋值语句

    {

    add("a");

    add("b");

    add("c");

    }

    }

    以上代码就是最开始那句代码的完整版,创建一个MyList类(名字随意),继承自ArrayList,并编写一个非静态代码块调用三次add方法,这个代码块将会在构造方法执行前被执行,因此创建一个MyList对象后,它肯定已经有三条数据了。若到此时还没有听懂,可能就需要去了解一下匿名内部类,以及代码块的执行机制了。

    这种为集合赋值的好处就是,它可以用在任意一种集合类型上(Map,Set......),如下代码:

    // 使用此方法为map赋值

    HashMap map = new HashMap() {

    {

    put("a", 1);

    put("b", 2);

    put("c", 3);

    }

    };

    当然,这种方法也有一些弊端,就拿ArrayList来说,那就是这种方法得到的对象,它的类型并不是ArrayList,我们调用对象.getClass().getName()方法获取对象的类名,得到的是代码所在类的类名+$1(这里和匿名内部类的机制有关,就不详细叙述了)。所以在代码中,如果对对象的类型有着严格的要求,就需要谨慎考虑是否应该使用这种方式。

    博客总结

    在平常编写代码时,还是第一种方式使用的比较多,因为简单而且不容易产生问题;而第二种方,我个人建议还是少用(虽然我就是为第二种方式写的博客.....),因为在类型要求严格的程序中,可能会产生问题。当然,第二种方式真的非常机智(感叹),而且可以用在各种类型的集合上,学习一下还是很有帮助的。

    参考文献

    《Java核心技术 卷Ⅰ》

    展开全文
  • 最近在写一些关于java基础的文章,但是...总结java创建文件夹的4种方法及其优缺点 总结java中删除文件或文件夹的7种方法 总结java中文件拷贝剪切的5种方式 比如之前我已经写了上面的这些内容,如果对java基础知识总结

    最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大。基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华。所以我更想把java相关的基础知识进行穿针引线,进行整体上的总结。

    总结java中创建并写文件的5种方式
    总结java从文件中读取数据的6种方法
    总结java创建文件夹的4种方法及其优缺点
    总结java中删除文件或文件夹的7种方法
    总结java中文件拷贝剪切的5种方式
    

    比如之前我已经写了上面的这些内容,如果对java基础知识总结系列感兴趣的同学可以关注我的博客(文末给出我的博客地址)。
    一、本文梗概

    这一篇文章我想写一下List集合元素去重的8种方法,实际上通过灵活的运用、排列组合不一定是8种,可能有18种方法。

    对象元素整体去重的4种方法
    按照对象属性去重的4种方法
    

    为了在下文中进行测试内容讲解,我们先做一些初始化数据

    public class ListRmDuplicate {
    private List list;
    private List playerList;

    @BeforeEach
    public void setup() {
    list = new ArrayList<>();
    list.add(“kobe”);
    list.add(“james”);
    list.add(“curry”);
    list.add(“zimug”);
    list.add(“zimug”);

    playerList= new ArrayList<>();
    playerList.add(new Player("kobe","10000"));  //科比万岁
    playerList.add(new Player("james","32"));
    playerList.add(new Player("curry","30"));
    playerList.add(new Player("zimug","27"));   // 注意这里名字重复
    playerList.add(new Player("zimug","18"));   //注意这里名字和年龄重复
    playerList.add(new Player("zimug","18")); //注意这里名字和年龄重复
    

    }
    }

    Player对象就是一个普通的java对象,有两个成员变量name与age,实现了带参数构造函数、toString、equals和hashCode方法、以及GET/SET方法。
    二、集合元素整体去重

    下文中四种方法对List中的String类型以集合元素对象为单位整体去重。如果你的List放入的是Object对象,需要你去实现对象的equals和hashCode方法,去重的代码实现方法和List<String>去重是一样的。
    

    第一种方法

    是大家最容易想到的,先把List数据放入Set,因为Set数据结构本身具有去重的功能,所以再将SET转为List之后就是去重之后的结果。这种方法在去重之后会改变原有的List元素顺序,因为HashSet本身是无序的,而TreeSet排序也不是List种元素的原有顺序。

    @Test
    void testRemove1() {
    /Set set = new HashSet<>(list);
    List newList = new ArrayList<>(set);
    /

    //去重并排序的方法(如果是字符串,按字母表排序。如果是对象,按Comparable接口实现排序)
    //List newList = new ArrayList<>(new TreeSet<>(list));

    //简写的方法
    List newList = new ArrayList<>(new HashSet<>(list));

    System.out.println( "去重后的集合: " + newList);
    }

    控制台打印结果如下:

    去重后的集合: [kobe, james, zimug, curry]

    第二种方法

    使用就比较简单,先用stream方法将集合转换成流,然后distinct去重,最后在将Stream流collect收集为List。

    @Test
    void testRemove2() {
    List newList = list.stream().distinct().collect(Collectors.toList());

    System.out.println( "去重后的集合: " + newList);
    }

    控制台打印结果如下:

    去重后的集合: [kobe, james, curry, zimug]

    第三种方法 这种方法利用了set.add(T),如果T元素已经存在集合中,就返回false。利用这个方法进行是否重复的数据判断,如果不重复就放入一个新的newList中,这个newList就是最终的去重结果

    //三个集合类list、newList、set,能够保证顺序
    @Test
    void testRemove3() {

    Set set = new HashSet<>();
    List newList = new ArrayList<>();
    for (String str :list) {
    if(set.add(str)){ //重复的话返回false
    newList.add(str);
    }
    }
    System.out.println( "去重后的集合: " + newList);

    }

    控制台打印结果和第二种方法一致。
    http://www.bjhfyjc.com
    第四种方法 这种方法已经脱离了使用Set集合进行去重的思维,而是使用newList.contains(T)方法,在向新的List添加数据的时候判断这个数据是否已经存在,如果存在就不添加,从而达到去重的效果。

    //优化 List、newList、set,能够保证顺序
    @Test
    void testRemove4() {

    List newList = new ArrayList<>();
    for (String cd:list) {
    if(!newList.contains(cd)){ //主动判断是否包含重复元素
    newList.add(cd);
    }
    }
    System.out.println( "去重后的集合: " + newList);

    }

    控制台打印结果和第二种方法一致。
    三、按照集合元素对象属性去重

    其实在实际的工作中,按照集合元素对象整体去重的应用的还比较少,更多的是要求我们按照元素对象的某些属性进行去重。 看到这里请大家回头去看一下上文中,构造的初始化数据playerList,特别注意其中的一些重复元素,以及成员变量重复。

    第一种方法 为TreeSet实现Comparator接口,如果我们希望按照Player的name属性进行去重,就去在Comparator接口中比较name。下文中写了两种实现Comparator接口方法:

    lambda表达式:(o1, o2) -> o1.getName().compareTo(o2.getName())
    方法引用:Comparator.comparing(Player::getName)
    

    @Test
    void testRemove5() {
    //Set playerSet = new TreeSet<>((o1, o2) -> o1.getName().compareTo(o2.getName()));
    Set playerSet = new TreeSet<>(Comparator.comparing(Player::getName));
    playerSet.addAll(playerList);

    /new ArrayList<>(playerSet).forEach(player->{
    System.out.println(player.toString());
    });
    /
    //将去重之后的结果打印出来
    new ArrayList<>(playerSet).forEach(System.out::println);
    }

    输出结果如下:三个zimug因为name重复,另外两个被去重。但是因为使用到了TreeSet,list中元素被重新排序。

    Player{name=‘curry’, age=‘30’}
    Player{name=‘james’, age=‘32’}
    Player{name=‘kobe’, age=‘10000’}
    Player{name=‘zimug’, age=‘27’}

    第二种方法 这种方法是网上很多的文章中用来显示自己很牛的方法,但是在笔者看来有点脱了裤子放屁,多此一举。既然大家都说有这种方法,我不写好像我不牛一样。我为什么说这种方法是“脱了裤子放屁”?

    首先用stream()把list集合转换成流
    然后用collect及toCollection把流转换成集合
    然后剩下的就和第一种方法一样了
    

    前两步不是脱了裤子放屁么?看看就得了,实际应用意义不大,但是如果是为了学习Stream流的使用方法,搞出这么一个例子还是有可取之处的。

    @Test
    void testRemove6() {
    List newList = playerList.stream().collect(Collectors
    .collectingAndThen(
    Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Player::getName))),
    ArrayList::new));

    newList.forEach(System.out::println);
    }

    控制台打印输出和第一种方法一样。
    http://www.chbjfxd.com
    第三种方法

    这种方法也是笔者建议大家使用的一种方法,咋一看好像代码量更大了,但实际上这种方法是应用比较简单的方法。

    Predicate(有人管这个叫断言,从英文的角度作为名词可以翻译为谓词,作为动词可以翻译为断言)。谓词就是用来修饰主语的,比如:喜欢唱歌的小鸟,喜欢唱歌就是谓词,用来限定主语的范围。所以我们这里是用来filter过滤的,也是用来限制主语范围的,所以我认为翻译为谓词更合适。随便吧,看你怎么觉得怎么理解合理、好记,你就怎么来。
    
    首先我们定义一个谓词Predicate用来过滤,过滤的条件是distinctByKey。谓词返回ture元素保留,返回false元素被过滤掉。
    当然我们的需求是过滤掉重复元素。我们去重逻辑是通过map的putIfAbsent实现的。putIfAbsent方法添加键值对,如果map集合中没有该key对应的值,则直接添加,并返回null,如果已经存在对应的值,则依旧为原来的值。
    如果putIfAbsent返回null表示添加数据成功(不重复),如果putIfAbsent返回value(value==null :false),则满足了distinctByKey谓词的条件元素被过滤掉。
    

    这种方法虽然看上去代码量增大了,但是distinctByKey谓词方法只需要被定义一次,就可以无限复用。

    @Test
    void testRemove7() {
    List newList = new ArrayList<>();
    playerList.stream().filter(distinctByKey(p -> p.getName())) //filter保留true的值
    .forEach(newList::add);

    newList.forEach(System.out::println);
    }

    static Predicate distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    //putIfAbsent方法添加键值对,如果map集合中没有该key对应的值,则直接添加,并返回null,如果已经存在对应的值,则依旧为原来的值。
    //如果返回null表示添加数据成功(不重复),不重复(null==null :TRUE)
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    输出结果如下:三个zimug因为name重复,另外两个被去重。并且没有打乱List的原始顺序

    Player{name=‘kobe’, age=‘10000’}
    Player{name=‘james’, age=‘32’}
    Player{name=‘curry’, age=‘30’}
    Player{name=‘zimug’, age=‘27’}

    第四种方法 第四种方法实际上不是新方法,上面的例子都是按某一个对象属性进行去重,如果我们想按照某几个元素进行去重,就需要对上面的三种方法进行改造。 我只改造其中一个,另外几个改造的原理是一样的,就是把多个比较属性加起来,作为一个String属性进行比较。

    @Test
    void testRemove8() {
    Set playerSet = new TreeSet<>(Comparator.comparing(o -> (o.getName() + “” + o.getAge())));

    playerSet.addAll(playerList);

    new ArrayList<>(playerSet).forEach(System.out::println);
    }
    http://www.bjwhnb.com
    欢迎关注我的博客,里面有很多精品合集

    展开全文
  • Java如何创建空的集合对象

    千次阅读 2021-03-22 17:50:32
    java.util.Collections实用工具类具有创建空的三个不同的静态常量List,Set和Map。Collections.EMPTY_LISTCollections.EMPTY_SETCollections.EMPTY_MAP当您要创建类型安全的空集合时,还有一些方法。Collections....

    有时您需要从Java方法返回一个空集合。java.util.Collections实用工具类具有创建空的三个不同的静态常量List,Set和Map。Collections.EMPTY_LIST

    Collections.EMPTY_SET

    Collections.EMPTY_MAP

    当您要创建类型安全的空集合时,还有一些方法。Collections.emptyList()

    Collections.emptySet()

    Collections.emptyMap()

    下面是代码示例。package org.nhooo.example.util;

    import java.util.*;

    public class EmptyCollectionDemo {

    public static void main(String args[]) {

    List list = Collections.EMPTY_LIST;

    System.out.println("list.size()  = " + list.size());

    Set set = Collections.EMPTY_SET;

    System.out.println("set.size()   = " + set.size());

    Map map = Collections.EMPTY_MAP;

    System.out.println("map.size()   = " + map.size());

    // 对于类型安全的示例,请使用以下方法。

    List strings = Collections.emptyList();

    System.out.println("strings.size = " + strings.size());

    Set longs = Collections.emptySet();

    System.out.println("longs.size() = " + longs.size());

    Map dates = Collections.emptyMap();

    System.out.println("dates.size() = " + dates.size());

    }

    }

    输出为:list.size()  = 0

    set.size()   = 0

    map.size()   = 0

    strings.size = 0

    longs.size() = 0

    dates.size() = 0

    展开全文
  • List集合批量添加对象属性

    千次阅读 2020-10-29 17:21:43
    List集合批量添加对象属性 在网上搜了几个文章,一言难尽,自己写一个 //对象集合 List<EsEntity> list = new ArrayList<>(); //通过Collections集合工具类进行批量添加 //第一个参数为存放得集合类型,...
  • android培训、java培训、期待与您交流! ---------- 生活是一面镜子,照着别人,也照着自己 1:对象数组 (1)数组既可以存储基本数据类型,也可以存储引用类型。它存储引用类型的时候的数组就叫对象数组。 (2)案例: 用...
  • javaList对象集合的遍历方法 第一种: for(Iterator<A> it = list.iterator(); it.hasNext(); ) { .... } 这种方式在循环执行过程中会进行数据锁定, 性能稍差, 同时,如果你想在寻欢过程中去掉某个元素,只能...
  • 首先新建一个实体类Person@Datapublic class Person {/** 编码 */private String code;/** 名字 */private String name;public Person(String code, String name) {this....}}实例化三个对象放入list集合中public s...
  • Java创建List的4种方法

    千次阅读 2021-04-25 19:04:05
    1、通过构造方法,例如:List<String>...但是这种方法构造出的List是固定长度的,如果调用add方法增加新的元素,会报异常:java.lang.UnsupportedOperationException,其实也可以理解,这个List是...
  • Java复制List集合

    万次阅读 2018-01-12 14:37:27
    JDK貌似没有自带的复制List集合的方法,因此,复制方法需要自己去实现。下面介绍两种简单实用的复制List集合的方法。 方法1、创建新的ArrayList集合时复制 public static void main(String[] args) {  List ...
  • java集合类的创建方式

    千次阅读 2020-12-17 14:31:06
    java集合类的创建方式 常常因为不会创建集合类的语法而浪费时间。...常见的集合类有List集合、Set集合、Map集合。   下面举一个实例,看看如何创建并添加修改集合元素。 1 import java.util.Iterator; 2 impor
  • java8 List,Map,对象集合常用操作

    千次阅读 2018-11-06 15:25:08
    //list 集合排序 List < String > names2 = new ArrayList < String > ( ) ; names2 . add ( "Google " ) ; names2 . add ( "Runoob " ) ; names2 . add ( "Taobao " ) ; names2 . add ( "Baidu " ) ; ...
  • List集合存储学生对象用三种方式遍历 ...②创建List集合对象 ③创建学生对象 ④把学生添加到集合 public class ListTest { public static void main(String[] args) { List<Student> arr = new...
  • java--list集合对象日期排序

    万次阅读 2018-10-22 15:26:38
    传入一个 集合对象 ,我的bean中时间属性是 date 类型; private static void ListSort(List&lt;JzdtInfo&gt; list) { { //排序方法 Collections.sort(list, new Comparator&lt;JzdtInfo&...
  • java-list集合

    千次阅读 2020-01-21 12:17:04
    ListJava中比较常用的集合类,关于List接口有很多实现类 ArrayList实现了List接口,是List的实现 ArrayList ArrayList底层是用数组实现的,可以认为ArrayList是一个可改变大小的数组。随着越来越多的元素被...
  • Java8 List集合过滤出符合条件的List元素集合 新增实体类 public class Student { private Integer stuNum; // 学号 private String name; // 姓名 private Integer age; // 年龄 省略set get 或加 @Data...
  • JavaList集合和枚举转换为数组解决方案最近在做项目的时候,经常遇到将将一个List集合中的对象的某个属性字段取出,然后组装成该字段属性对应类型的数组,也遇到了将一个枚举中每个元素对应的值取出,组装成该枚举...
  • 如果 List 集合对象由于调用 add 方法而发生更改,则返回 true;否则返回 false。 add(E e) 参数说明: e:要添加到列表中的元素。 示例 本示例使用 List 接口的实现类 ArrayList 初始化一个列表对象,然后调用...
  • Java快速创建List的4种方法

    千次阅读 2021-02-27 12:00:25
    1、通过构造方法,例如:List list = new ArrayList<>();然后调用list.add增加元素,如果知道初始...但是这种方法构造出的List是固定长度的,如果调用add方法增加新的元素,会报异常:java.lang.UnsupportedO...
  • 将数据添加到一个list集合中,最后提交之前去重。定义实体类:public class Robot implements Serializable {/*** id*/private Long id;/*** 名称*/private String name;}根据Robot实体中的id字段进行去重,代码...
  • java Gson与对象,Map集合List之间的相互转化前言建一个User类建一个GsonUtil工具类建一个Love测试类运行结果 前言 今天用的是gson工具类实现几个常用的gson转化和list转为对象这几个常用的。 建一个User类 public...
  • java 两个List集合各种情况对比处理

    千次阅读 2021-03-04 04:51:08
    Lambda作为函数式编程中的基础部分,在其他编程语言(例如:Scala)中早就广为使用,但在JAVA领域中发展较慢,直到java8,才开始支持Lambda。抛开数学定义不看,直接来认识Lambda。Lambda表达式本质上是匿名方法,其...
  • 想了解java8从list集合中取出某一属性的值的集合案例的相关内容吗,*饼饼*在本文为您仔细讲解java8 list取属性值集合的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:java8,list集合,属性值集合,下面大家...
  • 有时候在对List集合操作时并不想新建一个实体类来进行转换。这就涉及到将集合中对象的每个元素投影到新属性,以此来生成一个新的集合。 一、准备工作 定义实体类UserEntity @Data public class UserEntity ...
  • JavaList集合进行排序

    千次阅读 2019-03-29 15:53:27
    这几天做题刚好遇到对List集合排序的题,发现一种好用的方法,做一个笔记。 我是基于spring,对于刚学java的小伙伴可以直接在main方法中去完成。 第一步 创建一个实体类对象User package bean; public class User {...
  • java中声明一个全局得list变量得写法

    千次阅读 2019-09-11 16:58:28
  • public class TestAmount { public static void main(String[] args) { ...//创建3个对象,金额分别是7000、9000、8000,6000并将他们依次放入List中   Amount a1 = new Amount();   a1.set
  • 创建ArrayList集合对象并添加元素

    千次阅读 2021-02-12 21:34:55
    /** 为什么出现集合类:* 我们学习的是面向对象编程语言,而面向对象编程语言对事物的描述都是通过对象来体现的。* 为了方便对多个对象进行操作,我们就必须对这多个对象进行存储,而要想对多个对象进行存储,* 就不能...
  • 前言:在一次做项目的过程中要遍历list集合,然后根据条件删除list集合中不需要的对象,尝试了list.remove()方法,根本达不到目的,最后在网上看了几个帖子后才知道,要想根据条件删除list集合里面的对象,一定要...
  • 在项目开发的过程中,我们经常会对List集合进行按条件的过滤,筛选出我们想要的结果或者是符合项目需求的数据。比如:我们有一批学生对象,每个学生都有自己的年龄属性,但是我们想要筛选出这一批学生中年龄为21和22...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 318,462
精华内容 127,384
关键字:

java创建list集合对象

java 订阅