java 集合类

    xiaoxiao2021-04-19  80

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

    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 8 Name xiaoHongAge 10 Name xiaoMingAge 12 Name 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:1 Name:xiaoLi Age:8 Name:xiaoHong Age:10 Name:xiaoMing Age:12 Name: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---13209905673 map.Person@669d7d3---12344567890 map.Person@6e8c2c7---13967890984

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

    //输出的信息 map.Person@7852e922---13967890984 map.Person@15db9742---18542678894 map.Person@6d06d69c---12344567890 map.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---13209905673 8---ziyou---13967890984 11---liang---18542678894 17---qishi---12344567890

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

    转载请注明原文地址: https://ju.6miu.com/read-675747.html

    最新回复(0)