java 集合类

来源:互联网 发布:短信猫用什么网络 编辑:程序博客网 时间:2024/06/06 07:30

集合中存储的是对象的引用而非实体。

1,迭代器Iterator

迭代器Iterator是一个接口,但在每个Collection的子类中,都有已经实现Iterator的内部类。

ArrayList<String> al=new ArrayList();al.add("java1");al.add("java2");al.add("java3");al.add("java4");Iterator it=al.iterator();String str=iterator.next();//获取Iterator指向的下一个元素。/***通过Iterator遍历集合*/while(it.hasNext()){    Systetm.out.println(it.next());}/***通过Iterator遍历集合*/for(Iterator it=al.iterator();it.hasNext();){     Systetm.out.println(it.next());}

2,Set和List的区别

List:元素有序,元素可重复,因为给集合体系有索引。

Set:元素无序,不可重复。

注意:有序和无序是相对于存入和取出的顺序来判断的。Set集合遍历取出的元素和存入元素的顺序是没有任何关系的,所以称为无序。但是在Set存入的过程中,是有顺序的。比如HashSet按照HashCode对元素进行排序。TreeSet需要元素对象实现Comparable接口,自定义对不同的元素对象进行比较。TreeSet存入元素的过程中,按照自定义的方式对元素进行排序。所以遍历取出的顺序和存入的顺序就没有任何关系,所以称为无序。

3,List中的常用方法

增:add(),add(int index,E element),add(int index,Collection collection)

删:remove(int index)

改:set(int index,E element)

查:get(int index),subList(int from,int to),ListIterator();

3.1 ListIterator的用法

ListIterator是Iterator的子接口。
使用Iterator遍历集合时,不能再操作集合,例如增加元素或者修改,否则会出现并发异常。

但操作List集合时,可以使用ListIterator实现对集合中的元素的增删改查。

ArrayList<String> al=new ArrayList();al.add("java1");al.add("java2");al.add("java3");al.add("java4");ListIterator li=al.listIteraror();while(li.hasNext()){   String str=li.next();   if(str.equals("java2")){       li.add("hello world");//Iterator添加元素       li.set("c++");//Iterator修改元素,将java2修改为C++   }}

因为List有索引,所以ListIterator除了HasNext()从前向后遍历外,还有HasPrevious()方法从后向前遍历,对应的方法是previous();

3.2 LinkedList的特有方法

添加元素:addFirst(),addLast();新版本建议使用offerFirst(),offerLast()。因为当集合中无元素时旧方法抛异常,新昂发返回null,所以建议新方法,下同。

查询元素:getFirst(),getLast()。新方法:peekFirst(),peekLast();

删除元素:removeFirst(),removeLast()。新方法:poolFirst(),poolLast()。

3.3 删除ArrayList中的重复对象

eg:删除name和age相同的Person对象。
做法:重写Person类的equals()方法,age和name相同的两个Person对象即认为相同,返回true。

然后新建一个ArrayList,遍历过程中使用contains()方法判断放进来的元素是否已经存在。若存在,则不放入。List的contains方法会自动调用泛型对象的equals()方法来判断。
除此之外,remove(Objectobj)也调用泛型对象的equals()方法。所以,特殊的需求需要重写泛型对象的equals()方法。(String类型不需要,因为String已经重写了equals方法)

4,List中的子类区别

  • ArrayList:底层的数据结构是数组结构,特点:因为有角标,所以查询很快,但增删稍慢,线程不同步。
  • LinkedList:底层的数据结构是链表结构。特点:增删速度快,查询慢。
  • Vector:底层的数据结构是数组结构,线程同步,现在已经被ArrayList代替了。Vector支持枚举遍历,枚举和迭代器一样。现在在普遍采用迭代器。

可变长度数组:ArrayList是用长度可变的数组实现的,新建一个ArrayList对象,初始数组长度为10,当元素个数超过10时,重新创建一个数组,容量增加一半,长度为15。并且将原数组的元素赋值到新数组中来,以此类推。这就是可变长度数组的原理。

5.Set

5.1 HashSet

  • HashSet:底层数据结构是哈希表,遍历只能采用Iterator。HashSet保证元素唯一性的方式:

通过元素对象的两个方法:hashCode()和equals()方法。
如果元素的HashCode相同,那么调用equals方法判断元素对象内容是否相同。如果也相同,那么视为同一个对象。如果HashCode不同,那么直接存入。

自定义对象存入HashSet保证元素唯一性的方法:

下面的代码:

HashSet<Person> set=new HashSet();set.add(new Person("zhangsan",12));set.add(new Person("lisi",15));set.add(new Person("zhangsan",12));

通过Iterator遍历发现,这三个元素都存入到了HashSet中。这是因为这是三个独立的对象,Hash值不相同。

但是有两个对象属性是相同的。解决办法是:
重写Person类的hashCode()方法和equals()方法。
hashCode()的返回值可以返回一个常量,保证所有Person对象的hashcode值相同,这时HashSet会调用对象的equals()方法来判断元素对象内容是否相同。如果相同,视为同一元素,不会存入。

class Person{    String name;    int age;    public person(String name,int age){        this.name=name;        this.age=age;    }    public int hashCode(){       // return 56;//可以返回一个常量,保证每个对象的hashcode都相同。       return this.name.hashCode();//这样效率更高,可以省去很多不必要的重复判断。    }    public boolean equasl(Object obj){        if(!(obj instanceOf Person))        return false;        Person p=(Person) obj;        return this.name.equals(p.name)&&this.age==(p.age);    }}

HashSet存入的对象不能重复,如果对象的内容也不允许重复,那么必须重写对应的类的hashCode()方法和equals()方法。

HashCode判断元素是否存在以及remove元素,都会调用对象的hashCode()和equals()方法。
而ArrayList只会调用equals()方法。

5.2 TreeSet

TreeSet内部对存入的对象会进行排序,如果是自定义的对象,那么比较的规则需要用户实现。

TreeSet底层数据结构是二叉树(红黑树)。这种数据结构可以减少比较次数

TreeSet保证元素唯一性的依据:compareTo()方法返回0则不存入。

TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo()方法,这种方式成为元素
的自然数序,也称为默认数序

TreeSet排序的第二种方式:当元素自身不具备比较性时,就需要TreeSet自身具备比较性。具体做法是TreeSet的构造方法中,以
Comparator对象作为参数,Comparator子类实现比较。

TreeSet底层数据结构是二叉树(红黑树)。这种数据结构可以减少比较次数。

代码1:

public class main {    /**     * @param args     */    public static void main(String[] args) {        TreeSet<Demo> treeSet=new TreeSet<>();        treeSet.add(new Demo());        treeSet.add(new Demo());        treeSet.add(new Demo());        Iterator<Demo>iterator=treeSet.iterator();        while(iterator.hasNext()){            System.out.println(iterator.next().hashCode());        }    }}class Demo{}

运行报异常:Exception in thread “main” java.lang.ClassCastException: TreeSetTest.Demo cannot be cast to java.lang.Comparable。

所以自定义的对象存入TreeSet必须首先实现Comparable接口,自定义元素对象比较规则 。

代码2(元素对象具有比较性):

/** * 只按照年龄进行比较 * @author liang * */ class Student implements Comparable<Student>{     private String nameString;     private int age;     public Student(String nameString, int age) {        this.nameString = nameString;        this.age = age;    }    public String getNameString() {        return nameString;    }    public int getAge() {        return age;    }    /**     * Comparable 接口需要实现的方法,用于对两个元素进行排序     * 判断规则自定义,比如按照年龄进行排序或者姓名的字典顺序进行排序。     * 如果返回0,说明两个元素对象相同,TreeSet不会重复存入。如果返回值大于0,那么当前对象大于指定对象。反之小于指定对象。     */    @Override    public int compareTo(Student o) {        if(this.age>o.age)            return 1;        if(this.age==o.age)            return 0;        return -1;  //当前对象年龄小于指定对象,返回-1,    } } public class main {    public static void main(String[] args) {        TreeSet<Student>treeSet=new TreeSet<>();        treeSet.add(new Student("xiaoMing", 12));        treeSet.add(new Student("xiaoHong", 10));        treeSet.add(new Student("xiaoWang", 21));        treeSet.add(new Student("xiaoLi", 8));        Iterator< Student> iterator=treeSet.iterator();        while (iterator.hasNext()) {            Student student=iterator.next();            System.out.println("Name  "+student.getNameString()+"Age  "+student.getAge());        }    }}//打印顺序,已经按照年龄进行了排序。输出的顺序和存入的顺序没有任何关系。Name  xiaoLiAge  8Name  xiaoHongAge  10Name  xiaoMingAge  12Name  xiaoWangAge  21

实现TreeSet有序的方式

compareTo(T t)方法直接返回1即可。这样每存进一个元素,都比前者大。遍历取出的时候会由小到大取出。这样就实现了TreeSet集合的有序。

代码3(TreeSet以Comparator作为参数实现比较功能):

 class Student2{     private String nameString;     private int age;     public Student2(String nameString, int age) {        this.nameString = nameString;        this.age = age;    }    public String getNameString() {        return nameString;    }    public int getAge() {        return age;    } } class MyComparator implements Comparator<Student2>{     /**      * 只按照年龄进行比较      */    @Override    public int compare(Student2 o1, Student2 o2) {        if(o1.getAge()>o2.getAge())            return 1;        if(o1.getAge()==o2.getAge())        return 0;        return -1;    } } public static void main(String[] args) {        TreeSet<Student2>treeSet=new TreeSet<>(new MyComparator());        treeSet.add(new Student2("xiaoMing", 12));        treeSet.add(new Student2("xiaoHong", 10));        treeSet.add(new Student2("xiaoWang", 21));        treeSet.add(new Student2("xiaoLi", 8));        treeSet.add(new Student2("xiaoHe", 1));        Iterator< Student2> iterator=treeSet.iterator();        while (iterator.hasNext()) {            Student2 student=iterator.next();            System.out.println("Name:"+student.getNameString()+"  Age:"+student.getAge());        }    }

运行结果:

Name:xiaoHe  Age:1Name:xiaoLi  Age:8Name:xiaoHong  Age:10Name:xiaoMing  Age:12Name:xiaoWang  Age:21

6.泛型

泛型的本质是参数化类型,避免各种强转造成的安全性问题

6.1泛型方法

public class Fanxing{      public static <T> void out(T,t){                System.out.println(t);      }      public static void main(String[] args){              out("hansheng");              out(123);      }}

6.2泛型的限定

? 是通配符 指代 任意类型,和T或者E用法基本相同。

泛型的限定上限:
<? extends E> 接受 E 或者 E 的子类型。
泛型的限定下限:

    /**     * al的泛型是Person以及Person的子类     * @param al     */    void printData(ArrayList<? extends Person > al){    }    /**     * al的泛型是Student以及Student的父类     * @param al     */    void printData2(ArrayList<? super Student > al){    }
class Person{}class Student extends Person {}

7. Map集合

该集合存储键值对,必须保证键的唯一性。

7.1 Map中的常用方法

1,添加:
put(K key, V value) ;
putAll(Map

7.2 Map常见子类概述

|–HashTable:底层是哈希表数据结构,不可以存入null键和null值。该集合线程同步,jdk1.0出现的,效率低。

|–HashMap:底层是哈希表数据结构,允许使用null键和null值。该集合线程不同步,jdk1.2出现,效率高。

|–TeeeMap:底层是二叉树数据结构,线程不同步,可以用于给map集合中的键进行排序。

注意:Map和Set比较像,Set底层就是使用了Map集合。

7.3 遍历 Map

7.3.1:keySet()方法:

keySet()将Map中所有的键全部存放在Set集合中。根据Set集合中的键,就可以取出Map中的值。

    //Map中不能直接使用Iterator进行遍历。    static void testMap1(){        HashMap<String, String>map=new HashMap<>();        map.put("01", "唐僧");        map.put("02", "悟空");        map.put("03", "八戒");        map.put("04", "沙僧");        Set<String>mySet=map.keySet();        Iterator<String>iterator=mySet.iterator();        while (iterator.hasNext()) {            String key=iterator.next();            System.out.println(map.get(key));        }    }
//输出:唐僧悟空八戒沙僧
7.3.2 entrySet()方法

entrySet()方法返回的是一个Set集合,这个Set集合里面存放的是映射关系,而这个映射关系,用Map.Entry

    //Entry是Map接口中的静态内部接口。Map.Entry可以获取Map的键和值。    static void testMap2(){        HashMap<String, String>map=new HashMap<>();        map.put("01", "唐僧");        map.put("02", "悟空");        map.put("03", "八戒");        map.put("04", "沙僧");        Set<Map.Entry<String, String>> set=map.entrySet();        Iterator<Map.Entry<String, String>>iterator=set.iterator();         while (iterator.hasNext()) {                Map.Entry<String, String> entry=iterator.next();                System.out.println(entry.getKey()+"---"+entry.getValue());            }    }
//运行结果01---唐僧02---悟空03---八戒04---沙僧

7.4 HashMap举例:

HashMap的自定义对象最好实现hashCode()和equals()方法,以此确定判断元素重复的规则。

这一点和HashSet类似,因为底层都用哈希表实现的。元素之间判断是否重复会调用hashCode方法和equals方法。

/** * HashMap的自定义对象最好实现hashCode()和equals()方法,以此确定判断元素重复的规则。 * TreeMap的自定义对象必须实现Comparable接口或者TreeMap的构造方法中以Comparator对象作为参数。以此确定比较大小顺序的规则。 * @author liang * */class Person implements Comparable<Person>{    private int age;    private String name;    public Person(int age, String name) {        this.age = age;        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int hashCode() {        return name.hashCode()+age*5;    }    /**     * 只有名字和年龄都相同,才认为两个对象相同     */    @Override    public boolean equals(Object obj) {        if(!(obj instanceof Person))            throw new ClassCastException("类型不匹配");        Person  person=(Person)obj;        return this.name.equals(person.name)&&this.age==person.age;    }    /**     * 先比较年龄,如果两个年龄相同,那么按照字典顺序比较名字     * 如果年龄和姓名都相同,则返回0.     */    @Override    public int compareTo(Person o) {        int num=new Integer(this.age).compareTo(new Integer(o.age));        if(num==0)            return this.name.compareTo(o.name);        return num;    }}
    static void testMap3(){        HashMap<Person, String>map=new HashMap<>();        map.put(new Person(11, "liang"), "18542678894");        map.put(new Person(11, "qishi"), "12344567890");        map.put(new Person(11, "ziyou"), "13967890984");        map.put(new Person(11, "liang"), "13209905673");        Set<Map.Entry<Person, String>> set=map.entrySet();        Iterator<Map.Entry<Person, String>>iterator=set.iterator();         while (iterator.hasNext()) {                Map.Entry<Person, String> entry=iterator.next();                System.out.println(entry.getKey()+"---"+entry.getValue());            }    }

添加了4个元素,其中第一个元素和第四个元素age和name相同。按照比较规则,视为同一元素,所以第四个元素覆盖掉了第一个元素。

//输出的信息(为了简单,直接打印对象了。)map.Person@6231f74---13209905673map.Person@669d7d3---12344567890map.Person@6e8c2c7---13967890984

在Person类中,如果注释掉hashCode()方法和equals()方法,那么打印结果如下:

//输出的信息map.Person@7852e922---13967890984map.Person@15db9742---18542678894map.Person@6d06d69c---12344567890map.Person@4e25154f---13209905673

因为每一个新的对象哈希值都不会相同,所以HashMap认为是不同的元素。因此即使age和name相同,也被HashMap视为不同的元素

7.4 TreeMap举例:

TreeMap自定义对象的用法和TreeSet基本一致,要不元素对象实现Comparable接口,确定比较规则。
要么TreeMap的构造方法以Comparator子类对象作为参数,确定元素对象的比较规则。
如果比较规则没有实现相应的类或者参数,那么装入元素直接报异常。

class Person implements Comparable<Person>{    private int age;    private String name;    public Person(int age, String name) {        this.age = age;        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int hashCode() {        return name.hashCode()+age*5;    }    /**     * 只有名字和年龄都相同,才认为两个对象相同     */    @Override    public boolean equals(Object obj) {        if(!(obj instanceof Person))            throw new ClassCastException("类型不匹配");        Person  person=(Person)obj;        return this.name.equals(person.name)&&this.age==person.age;    }    /**     * 先比较年龄,如果两个年龄相同,那么按照字典顺序比较名字     * 如果年龄和姓名都相同,则返回0.视为同一元素     */    @Override    public int compareTo(Person o) {        int num=new Integer(this.age).compareTo(new Integer(o.age));        if(num==0)            return this.name.compareTo(o.name);        return num;    }}
    static void testMap4(){        TreeMap<Person, String>map=new TreeMap<>();        map.put(new Person(11, "liang"), "18542678894");        map.put(new Person(17, "qishi"), "12344567890");        map.put(new Person(8, "ziyou"), "13967890984");        map.put(new Person(2, "liang"), "13209905673");        Set<Map.Entry<Person, String>> set=map.entrySet();        Iterator<Map.Entry<Person, String>>iterator=set.iterator();         while (iterator.hasNext()) {                Map.Entry<Person, String> entry=iterator.next();                System.out.println(entry.getKey().getAge()+"---"+entry.getKey().getName()+"---"+entry.getValue());            }    }
//输出结果2---liang---132099056738---ziyou---1396789098411---liang---1854267889417---qishi---12344567890

可见TreeMap已经做了排序,优先按照age排序,如果age相同,那么再比较name的字典顺序。

0 0