【java】集合框架和map基础

来源:互联网 发布:tk 域名 编辑:程序博客网 时间:2024/04/29 21:56

一、概述

        java.util.Collection是java集合框架的根类,表示单列的数据结构,而java.util.Map接口代表了两列类型的数据结构——键值对。另外,java集合框架中只能存放对象,而不能存放基本数据类型,在JDK1.5以后增加了基本数据类型自动拆封箱,因此存入基本类型数据到集合中的时候,会自动装箱,实际上存入集合的还是对象而不是基本数据类型。



二、List

        存入List中的元素有索引,因此List中存放的元素有序可重复。Collecton中定义的迭代器默认只能向后读取,但因为List有索引,因此遍历效率高,所以除了基类中的迭代还有自己特定的迭代方式ListIterator。

        注意:List中contains判断是是否包含元素,是根据元素自身的equals方法来判断。

  • ArrayList和Vector

        ArrayList底层使用数组结构,因此对于ArrayList对象的特点有:因为基于数组有索引,而数组在内存中会开辟连续的存储空间(用于存放对象引用),并且基于下标允许随机访问任意元素,所以集合遍历效率高,查找、修改元素效率高,但又正因为底层基于数组,因此增加和删除元素效率极低。

        Vector底层也是用了数组结构,而且Vector的功能和ArrayList几乎一模一样,只是ArrayList是线程不安全的而Vector是同步的,因此ArrayList效率较高;除此之外,java api文档中指出,Vector是JDK1.0的数据结构,在JDK1.2中已经被改进的ArrayList替代;再者,对于Vector和ArrayList的迭代来说,两者都是快速失败(快速失败:如果在迭代器创建后的任意时间从结构上修改了向量——通过迭代器自身的 remove 或 add 方法之外的任何其他方式——则迭代器将抛出 ConcurrentModificationException),但是Vector中的elements方法返回的Enumeration不是快速失败的;最后,对于两者的初始化来说,ArrayList默认长度是10,超出以后会按照50%的速度递增,Vector默认长度也是10,不过超出后的递增速度是100%,所以如果能提前确认list的长度,直接用ensureCapacity来增加提高效率,否则每增加一次长度都会new新的数组在拷贝数据,导致效率低下。

        因此,一般情况下,推荐使用ArrayList而不是Vector。

package list;import java.util.ArrayList;import java.util.Iterator;import java.util.ListIterator;public class ArrayListDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();for(int i=0; i<5; i++)list.add("元素"+i);/* * for循环遍历list */for(int i=0; i<list.size(); i++)System.out.print(list.get(i)+" ");System.out.println();/* * foreach循环遍历list */for(String i:list)System.out.print(i+" ");System.out.println();/* * Iterator遍历list */for(Iterator<String> it=list.iterator(); it.hasNext(); )System.out.print(it.next() + " ");System.out.println();/* * List特有:ListIterator遍历list */ListIterator<String> it=list.listIterator();for(; it.hasNext(); )System.out.print(it.next() + " ");for(; it.hasPrevious(); )System.out.print(it.previous() + " ");}}
package list;import java.util.Enumeration;import java.util.Iterator;import java.util.ListIterator;import java.util.Vector;public class VectorDemo {public static void main(String[] args) {Vector<String> vector = new Vector<>();for(int i=0; i<5; i++)vector.add("元素"+i);/* * for循环遍历vector */for(int i=0; i<vector.size(); i++)System.out.print(vector.get(i) + " ");System.out.println();/* * foreach循环遍历vector */for(String i:vector)System.out.print(i + " ");System.out.println();/* * Iterator遍历vector */for(Iterator<String> it=vector.iterator(); it.hasNext(); )System.out.print(it.next() + " ");System.out.println();/* * List特有:ListIterator遍历vector */ListIterator<String> it = vector.listIterator();for(; it.hasNext(); )System.out.print(it.next() + " ");for(; it.hasPrevious(); )System.out.print(it.previous() + " ");System.out.println();/* * Vector独有:elements */for(Enumeration<String> e=vector.elements(); e.hasMoreElements(); )System.out.print(e.nextElement() + " ");}}

  • LinkedList

        LinkedList底层使用链表结果,因此对于LinkedList对象的特点有:因为底层使用链表结构,而链表在内存地址上可以不连续,只有相通过相邻元素才能进行查找遍历,因此查找、修改、遍历的效率较低,但又正因为使用了链表结构,因此在增加、删除元素的时候,只需要修改相邻元素存有的地址即可,故而删除、增加的效率较高。

        因为LinkedList使用了链表数据结构,因此插入元素的时候有头插法addFirst和尾插法addLast,默认add和offer是添加到尾部;获取元素有get(int index),element和getFirst获取第一个元素,getLast获取最后一个元素。LinkedList也是下了Queue接口,因此也有队列的方法,offer插入元素,peek获取栈顶元素但不删除,poll获取但删除等,LinkedList太杂了,使用的时候还是看看开发文档吧。

package list;import java.util.Iterator;import java.util.LinkedList;public class LinkedListDemo {public static void main(String[] args) {LinkedList<String> list = new LinkedList<>();for(int i=0; i<5; i++)list.add("元素"+i);for(Iterator<String> it=list.iterator(); it.hasNext(); )System.out.print(it.next() + " ");System.out.println(list.peek());System.out.println(list.peekFirst());System.out.println(list.poll());System.out.println(list);}}

三、Set

        set的特点是存入的元素无序不重复,Set的底层使用了Map。

  • HashSet

        HashSet底层结构是哈希表,存入元素无序不允许重复,当第二个元素及后续的元素存入HashSet中的时候,会首先判断该元素的哈希值是否与集合中的元素有相同,不同则存入,相同则调用元素的equals方法判断,false则存入,true则存入失败。当两个元素的哈希值相同时,在set中存放的位置相同,会根据存入的先后顺序在该位置存放链表,这样虽然能保证元素的存入但是会导致Set集合性能低下。因此当要存入自定义对象到HashSet中的时候,应该要重写该对象的hashCodeequals方法,保证hashCode相同的元素equals方法返回true。

       特别注意:HashSet中保证元素唯一性是根据存入元素的hashCode和equals方法。


        为了保证HashSet良好的性能,因此要求存入HashSet中的元素都要重写hashCode和equals方法,要求hashCode相等则equals也必须相等,代码示例如下。

package set;/* * 存入HashSet中的元素,重写hashCode和equals * 要求:hashCode相等则equals也必须相等 */public class Person {private String name;private int age;private String gender;public Person(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}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;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}/* * 重写equals方法 */@Overridepublic boolean equals(Object obj) {// TODO Auto-generated method stubif(this == obj)return true;if (!(obj instanceof Person))return false;Person per = (Person) obj;return this.name.equals(per.getName()) && this.age == per.getAge() && this.gender.equals(per.getGender());}/* * 重写hashCode方法 */@Overridepublic int hashCode() {// TODO Auto-generated method stubreturn this.name.hashCode()+age+this.gender.hashCode();}}
package set;import java.util.HashSet;public class HashSetDemo {public static void main(String[] args) {HashSet<Person> set = new HashSet<>();set.add(new Person("zhangsan", 10, "male"));set.add(new Person("zhangsan", 10, "male"));set.add(new Person("lisi", 20, "female"));System.out.println(set);/* * 输出结果: * [lisi--20--female, zhangsan--10--male] */}}

  • TreeSet

        TreeSet底层使用二叉树结构,因此TreeSet能够对放入集合中的元素进行排序,这就要求存入的对象具有比较性或告知TreeSet如何排序,对应着自然排序和定制排序。通常情况下,如果定制排序和自然排序同时存在,则定制排序会覆盖自然排序,即定制排序具有更高的优先级。

        TreeSet作为Set类型,也具备了Set类型的特点:存入元素无序不可重复。无序——虽然TreeSet会对存入元素进行排序,但是元素存入的顺序和元素在集合中保存的顺序是不一致的,不可重复——HashSet通过hashCode和equals方法保证元素的唯一性,而TreeSet则通过排序来保证元素的唯一性。

        1. 自然排序:要求存入TreeSet中对象元素自身具有比较性,实现java.lang.Comparable接口,并重写compareTo方法,this.compareTo(Object obj)返回值等于0,则表示集合中已存有相同元素,则拒绝存入;否则按照compareTo返回值进行存放并排序,其返回值大于0则表示this大obj,返回值小于0,则表示this小于obj。

        2. 定制排序:当自然排序和定制排序同时存在的时候,定制排序会覆盖自然排序,定制排序要求在实例化HashSet对象的时候,传入一个比较器,该比较器实现了java.util.Comparator接口并重写了compare(Object obj1, Object obj2)方法,如果obj1等于obj2,则返回0,如果obj1大于obj2则返回值大于0,如果obj1o小于bj2则返回值小于0;

        特别注意:TreeSet中保证元素的唯一性是根据元素自身实现的compareTo方法或根据TreeSet的比较器(比较器的compare方法)。

        自然排序实现代码示例如下:

package set;/* * 自然排序:存入TreeSet中的元素,必须实现java.lang.Comparable接口 * 要求:hashCode相等则equals也必须相等 */public class Person implements Comparable<Person>{private String name;private int age;private String gender;public Person(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}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;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn this.name + "--" + this.age + "--" + this.gender;}@Overridepublic int compareTo(Person per) {// TODO Auto-generated method stubreturn this.name.compareTo(per.getName()) + this.gender.compareTo(per.getGender()) + this.age - per.age;}}
package set;import java.util.TreeSet;public class TreeSetDemo {public static void main(String[] args) {TreeSet<Person> set = new TreeSet<>();set.add(new Person("zhangsan", 10, "male"));set.add(new Person("zhangsan", 10, "male"));set.add(new Person("lisi", 20, "female"));System.out.println(set);/* * 输出结果: * [lisi--20--female, zhangsan--10--male] */}}

        定制排序实现示例代码如下:

package set;public class Per {private String name;private int age;private String gender;public Per(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}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;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn this.name + "--" + this.age + "--" + this.gender;}}
package set;import java.util.Comparator;import java.util.TreeSet;public class TreeSetDemo {public static void main(String[] args) {TreeSet<Per> set = new TreeSet<>(new Comparator<Per>() {/* * 重写compare方法 */@Overridepublic int compare(Per per1, Per per2) {// TODO Auto-generated method stubreturn per1.getName().compareTo(per2.getName()) + per1.getGender().compareTo(per2.getGender()) + per1.getAge() - per2.getAge();}});set.add(new Per("zhangsan", 10, "male"));set.add(new Per("zhangsan", 10, "male"));set.add(new Per("lisi", 20, "female"));System.out.println(set);/* * 输出结果: * [lisi--20--female, zhangsan--10--male] */}}
        当自然排序和定制排序同时存在的时候,定制排序会覆盖自然排序,如下例,自然排序只要求姓名和性别比较,而定制排序要求姓名、性比和年龄都进行比较:
package set;/* * 自然排序:存入TreeSet中的元素,必须实现java.lang.Comparable接口 * 要求:hashCode相等则equals也必须相等 */public class Person implements Comparable<Person>{private String name;private int age;private String gender;public Person(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}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;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn this.name + "--" + this.age + "--" + this.gender;}@Overridepublic int compareTo(Person per) {// TODO Auto-generated method stub/* * 自然排序只要求姓名和性别相同 */return this.name.compareTo(per.getName()) + this.gender.compareTo(per.getGender());}}
package set;import java.util.Comparator;import java.util.TreeSet;public class TreeSetDemo {public static void main(String[] args) {TreeSet<Per> set = new TreeSet<>(new Comparator<Per>() {/* * 重写compare方法 */@Overridepublic int compare(Per per1, Per per2) {// TODO Auto-generated method stubreturn per1.getName().compareTo(per2.getName()) + per1.getGender().compareTo(per2.getGender()) + per1.getAge() - per2.getAge();}});set.add(new Per("zhangsan", 10, "male"));set.add(new Per("zhangsan", 12, "male"));set.add(new Per("lisi", 20, "female"));System.out.println(set);/* * 输出结果: * [lisi--20--female, zhangsan--10--male, zhangsan--12--male] */}}

四、Map

        Collection中都是存储单列元素,而Map则存储双列元素。

        Map用于存储具有映射关系的数据,因此Map集合一个元素中保存着两个值,一个是Key,一个是Value,Key和Value存在一对一的关系(通过制定的Key有且只有一个Value与其对应)并且两者可以是任何数据类型,包括基本数据类型(Collection是不允许存入基本数据类型的,存入的基本类型都会被自动装箱成相应的对象),Map中的Key基于Set实现,因此Map中的Key无序并且不允许重复。另外,Map接口提供三种collection视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。

        对于Map的存放,put存入元素的键值,返回值不是boolean类型,而是value;对于Map的删除,根据指定的Key删除对应的value,返回值不是boolean,也是所删除的key对应的value;

  • HashMap和HashTable

        HashMap和HashTable都是基于哈希表结构,两者非常相似,具有以下区别:

        1. HashTable在JDK1.0中出现,而HashMap作为HashTable的改进版本在JDK1.2中出现;

        2. HashMap允许存入null键null值,而HashTable不允许使用null键null值;

        3. HashMap线程不安全,而HashTable是线程安全,因此HashMap性能较高,通常情况下,推荐使用HashMap而不是HashTable;

        4. HashTable由于出现在早期的JDK,因此包含了elements和keys两个比较老的方法用于返回值集和键集,现在一般使用values和keySet两个方法代替;

        重点:HashMap和HashTable通过value对象自身的equals方法判断是否相等,而保证Key对象的唯一性是通的过自身的HashCode和equals方法判断是否相等,判断规则和HashSet一致。因此如果要自定义存入map中的Key,需要重写这两个方法,如果自定义的对象是可变的,则在存入后修改对象有可能获取不到元素。

package map;import java.util.Collection;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;public class HashMapDemo {public static void main(String[] args) {HashMap<String, String> map = new HashMap<>();map.put("001", "zhangsan");map.put("002", "lisi");map.put("003", "wanger");map.put("004", "mazi");/* * map删除元素返回值不是true/false,而是删除key的value * 成功删除004,将会返回mazi */System.out.println(map.remove("004"));/* * HashMap允许null键null值 */map.put(null, null);System.out.println(map);/* * values方法获取map中所有值 */Collection<String> valuesCol = map.values();for(Iterator<String> it=valuesCol.iterator(); it.hasNext(); )System.out.println(it.next());/* * keySet方法获取map中所有Key */Set<String> keySet = map.keySet();for(Iterator<String> it=keySet.iterator(); it.hasNext(); )System.out.println(it.next());/* * Map中定义的内部类Map.Entry的一个实例表示一个元素(键值对) * entrySet获取map中所有的元素(键值对) */Set<Map.Entry<String, String>> entrySet = map.entrySet();for(Iterator<Map.Entry<String, String>> it=entrySet.iterator(); it.hasNext(); ){Map.Entry<String, String> entry = it.next();System.out.println(entry.getKey()+" = "+entry.getValue());}}}
package map;import java.util.Collection;import java.util.Enumeration;import java.util.Hashtable;import java.util.Iterator;import java.util.Map;import java.util.Set;public class HashTableDemo {public static void main(String[] args) {Hashtable<String, String> map = new Hashtable<>();map.put("001", "zhangsan");map.put("002", "lisi");map.put("003", "wanger");map.put("004", "mazi");/* * map删除元素返回值不是true/false,而是删除key的value * 成功删除004,将会返回mazi */System.out.println(map.remove("004"));/* * Hashtable不允许null键null值,这句将报异常:java.lang.NullPointerException * map.put(null, null); * System.out.println(map); *//* * values方法获取map中所有值 */Collection<String> valuesCol = map.values();for(Iterator<String> it=valuesCol.iterator(); it.hasNext(); )System.out.println(it.next());/* * keySet方法获取map中所有Key */Set<String> keySet = map.keySet();for(Iterator<String> it=keySet.iterator(); it.hasNext(); )System.out.println(it.next());/* * Map中定义的内部类Map.Entry的一个实例表示一个元素(键值对) * entrySet获取map中所有的元素(键值对) */Set<Map.Entry<String, String>> entrySet = map.entrySet();for(Iterator<Map.Entry<String, String>> it=entrySet.iterator(); it.hasNext(); ){Map.Entry<String, String> entry = it.next();System.out.println(entry.getKey()+" = "+entry.getValue());}/* * HashTable中特有的方法elements和keys */for(Enumeration<String> en = map.elements(); en.hasMoreElements(); ){System.out.println(en.nextElement());}for(Enumeration<String> en = map.keys(); en.hasMoreElements(); ){System.out.println(en.nextElement());}}}

  • TreeMap

        TreeMap基于红黑树结构,线程不同步,可以给Map中的键进行排序。

        重点:TreeMap中的values对象通过自身的equals判断是是否相同,而保证Key对象的唯一性是通过自然排序(Key实现java.lang.Comparable重写compareTo方法)和定制排序(在建立Map实例的时候传入比较器,该比较器实现了java.util.Compator并重写compare方法)。

        1. 自然排序

        和TreeSet类似,要做为TreeMap的key,则要求该类实现了java.lang.Comparable接口,并重写compareTo方法

package map;/* * 自然排序:存入TreeMap中的元素,必须实现java.lang.Comparable接口并重写compareTo方法 */public class Person implements Comparable<Person>{private String name;private int age;private String gender;public Person(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}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;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn this.name + "--" + this.age + "--" + this.gender;}@Overridepublic int compareTo(Person per) {// TODO Auto-generated method stub/* * 自然排序只要求姓名和性别相同 */return this.name.compareTo(per.getName()) + this.gender.compareTo(per.getGender()) + (this.age - per.getAge());}}
package map;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.TreeMap;public class TreeMapDemo {public static void main(String[] args) {TreeMap<Person, String> map = new TreeMap<>();map.put(new Person("zhangsan", 10, "male"), "001");map.put(new Person("zhangsan", 10, "male"), "002");map.put(new Person("lisi", 20, "female"), "003");Set<Map.Entry<Person, String>> entrySet = map.entrySet();for(Iterator<Map.Entry<Person, String>> it=entrySet.iterator(); it.hasNext(); ){Map.Entry<Person, String> entry = it.next();System.out.println(entry.getKey()+"--"+entry.getValue());}/*输出结果:lisi--20--female--003zhangsan--10--male--002*/}}

        2. 定制排序

        和TreeSet一样,要求在实例化Map对象的时候,传入指定的比较器,该比较器实现了java.util.Compator接口,并重写了compare方法,示例代码如下。

package map;import java.util.Comparator;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.TreeMap;public class TreeMapDemo {public static void main(String[] args) {TreeMap<Person, String> map = new TreeMap<>(new Comparator<Person>() {@Overridepublic int compare(Person per1, Person per2) {// TODO Auto-generated method stubreturn per1.getName().compareTo(per2.getName()) + per1.getGender().compareTo(per2.getGender()) + per1.getAge() - per2.getAge();}});map.put(new Person("zhangsan", 10, "male"), "001");map.put(new Person("zhangsan", 10, "male"), "002");map.put(new Person("lisi", 20, "female"), "003");Set<Map.Entry<Person, String>> entrySet = map.entrySet();for(Iterator<Map.Entry<Person, String>> it=entrySet.iterator(); it.hasNext(); ){Map.Entry<Person, String> entry = it.next();System.out.println(entry.getKey()+"--"+entry.getValue());}/*输出结果:lisi--20--female--003zhangsan--10--male--002*/}}

五、Collections和Arrays

  • Collections

        Collections是集合框架的工具类,里面封装了对集合进行操作的静态方法。使用Collections,可以对List进行排序(基于对象的自然排序或自定义比较器传入sort方法),可以将线程不安全的List/Set/Map转变成程安全的List/Set/Map等等,具体详情查看一下开发文档即可。

  • Arrays

        Arrays是用于操作数组的工具类,里面封装了对数组进行操作的静态方法。使用ArrayList,可以对数组进行排序、查找等相关功能,具体的使用查看开发文档即可。

六、集合和数组互相转换

        1. 通过Arrays的静态方法asList可以将数组转换成List来操作,这样可以使用List的方法来操作原来的数组对象,但是要注意转换之后的List对象不能进行增删等导致List结构变化的操作,除此之外,还需要注意如果数组存储的是引用类型,则转换成List的时候数组中的元素会直接专程集合中的元素,但如果数组中存储的是基本类型的数据,则会将数组本身作为一个元素存入List,而不会将基本数据类型的元素自动装箱转成集合中的元素;

        2. 通过Collection自身的toArray方法可以将集合转换成数组,这样可以限定集合对元素的操作,因为集合可以进行增删等操作导致集合的结构变化,如果转成数组则将保持其结构,但是要注意,调用头Array泛型方法时需要传入一个数组,如果传入的数组长度小于集合长度,则会重新new一个数组,如果传入的数组长度大于集合长度,则多出来的部分传入默认值null;

        实现代码如下所示。

package collections;import java.util.ArrayList;import java.util.Arrays;import java.util.List;public class CollectionsDemo {/* * Arrays */public void arraysDemo(){Integer[] intObj = new Integer[]{1, 3, 5, 7};List<Integer> list = Arrays.asList(intObj);System.out.println(list);//输出结果:[1, 3, 5, 7]int[] intBase = new int[]{1, 3, 5, 7};List<?> list2 = Arrays.asList(intBase);//实际上是int[]类型System.out.println(list2);//输出结果:[[I@7852e922]}/* * Collections */public void collecionsDemo(){ArrayList<String> list = new ArrayList<>();list.add("1");list.add("2");list.add("3");//传入的数组长度小于集合长度String[] shortArr = list.toArray(new String[0]);for(String i:shortArr)System.out.print(i + " ");//输出结果:1 2 3System.out.println();//传入的数组长度大于集合长度String[] longArr = list.toArray(new String[5]);for(String i:longArr)System.out.print(i + " ");//输出结果:1 2 3 null null }public static void main(String[] args) {CollectionsDemo demo = new CollectionsDemo();demo.arraysDemo();demo.collecionsDemo();}}


附注:

        本文如有错漏之处,烦请不吝指正,谢谢!

0 0
原创粉丝点击