首页 > 编程 > Java > 正文

Java基础:集合框架

2019-11-06 07:06:58
字体:
来源:转载
供稿:网友

java类中集合的关系图

集合类概述

1、为什么出现集合类?

面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,Java就提供了集合类。

2、数组和集合类同是容器,有何不同?

3、集合类的特点

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

Collection接口概述

Collection 层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。

Collection接口成员方法

Collection实现类

List接口概述

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与 set 不同,列表通常允许重复的元素。

ArrayList类概述

底层数据结构是数组,查询快,增删慢,线程不安全,效率高。

Vector类概述

底层数据结构是数组,查询快,增删慢,线程安全,效率低。

Vector类特有功能

public void addElement(E obj):添加元素public E elementAt(int index):根据索引获取元素public Enumeration elements():获取所有的元素

LinkedList类概述

底层数据结构是链表,查询慢,增删快,线程不安全,效率高

方法声明 功能描述
public void addFirst(E e) 将指定元素插入此列表的开头
public voidaddLast(E e) 将指定元素添加到此列表的结尾
public E getFirst() 将指定元素添加到此列表的结尾
public E getLast() 获取集合的最后一个元素
public E removeFirst() 删除集合的第一个元素
public E removeLast() 删除最后一个元素

代码示例:使用LinkedList来模拟一个栈数据结构

package cn.itcast;import java.util.LinkedList;/* *使用LinkedList模拟栈数据结构的集合,并测试 *1、栈的特点先进后出 *2、 LinkedList的特有添加功能addFirst() */class MyStack { PRivate LinkedList link; public MyStack() { link = new LinkedList(); } public void add(Object obj) { // 将指定元素插入此列表的开头 link.addFirst(obj); } public Object get() { // 移除并返回此列表的第一个元素。 // return link.getFirst(); return link.removeFirst(); } public boolean isEmpty() { return link.isEmpty(); }}/* * MyStack的测试 */public class MyStackDemo { public static void main(String[] args) { // 创建集合对象 MyStack ms = new MyStack(); // 添加元素 ms.add("hello"); ms.add("world"); ms.add("java"); ms.add("android"); ms.add("javase"); while (!ms.isEmpty()) { System.out.println(ms.get()); } }}

运行结果:

2、Set接口概述

一个不包含重复元素的 collection,无序。

哈希表确定元素是否相同

1、 判断的是两个元素的哈希值是否相同。 如果相同,再判断两个对象的内容是否相同。

2、 判断哈希值相同,其实判断的是对象的HashCode方法。判断内容相同,用的是equals方法。

HashSet类概述

不保证 set 的迭代顺序,特别是它不保证该顺序恒久不变。HashSet如何保证元素唯一性底层数据结构是哈希表(元素是链表的数组)哈希表依赖于哈希值存储添加功能底层依赖两个方法:int hashCode()、boolean equals(Object obj)

HashSet存储元素保证唯一性的代码及图解:

package cn.itcast;import java.util.HashSet;class Dog { private String name; private int age; private String color; private char sex; public Dog() { super(); } public Dog(String name, int age, String color, char sex) { super(); this.name = name; this.age = age; this.color = color; this.sex = sex; } 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 getColor() { return color; } public void setColor(String color) { this.color = color; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((color == null) ? 0 : color.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + sex; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Dog other = (Dog) obj; if (age != other.age) return false; if (color == null) { if (other.color != null) return false; } else if (!color.equals(other.color)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (sex != other.sex) return false; return true; }}/* * HashSet集合存储自定义对象并遍历。如果对象的成员变量值相同即为同一个对象 * * 注意了: 你使用的是HashSet集合,这个集合的底层是哈希表结构。 而哈希表结构底层依赖:hashCode()和equals()方法。 * 如果你认为对象的成员变量值相同即为同一个对象的话,你就应该重写这两个方法。 如何重写呢?不同担心,自动生成即可。 */public class DogDemo { public static void main(String[] args) { // 创建集合对象 HashSet<Dog> hs = new HashSet<Dog>(); // 创建狗对象 Dog d1 = new Dog("秦桧", 25, "红色", '男'); Dog d2 = new Dog("高俅", 22, "黑色", '女'); Dog d3 = new Dog("秦桧", 25, "红色", '男'); Dog d4 = new Dog("秦桧", 20, "红色", '女'); Dog d5 = new Dog("魏忠贤", 28, "白色", '男'); Dog d6 = new Dog("李莲英", 23, "黄色", '女'); Dog d7 = new Dog("李莲英", 23, "黄色", '女'); Dog d8 = new Dog("李莲英", 23, "黄色", '男'); // 添加元素 hs.add(d1); hs.add(d2); hs.add(d3); hs.add(d4); hs.add(d5); hs.add(d6); hs.add(d7); hs.add(d8); // 遍历 for (Dog d : hs) { System.out.println(d.getName() + "---" + d.getAge() + "---" + d.getColor() + "---" + d.getSex()); } }}

运行结果:

LinkedHashSet类概述

元素有序唯一,由链表保证元素有序,由哈希表保证元素唯一。

TreeSet类概述

使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

TreeSet是如何保证元素的排序和唯一性 底层数据结构是红黑树(红黑树是一种自平衡的二叉树)

TreeSet判断元素唯一性的方式 就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。

TreeSet对元素进行排序的方式一 让元素自身具备比较功能,元素就需要实现Comparable接口,覆盖compareTo方法。 如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?

可以使用TreeSet集合第二种排序方式 让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。将该类对象作为参数传递给TreeSet集合的构造函数。

TreeSet存储元素自然排序和唯一的图解:

package cn.itcast;import java.util.Comparator;import java.util.TreeSet;class Student { private String name; private int age; public Student() { super(); } public Student(String name, int age) { super(); this.name = name; this.age = age; } 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; }}class MyComparator implements Comparator<Student> { public int compare(Student s1, Student s2) { // int num = this.name.length() - s.name.length(); // this -- s1 // s -- s2 // 姓名长度 int num = s1.getName().length() - s2.getName().length(); // 姓名内容 int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; // 年龄 int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; return num3; }}/* * 需求:请按照姓名的长度排序 * * TreeSet集合保证元素排序和唯一性的原理 唯一性:是根据比较的返回是否是0来决定。 排序: A:自然排序(元素具备比较性) * 让元素所属的类实现自然排序接口 Comparable B:比较器排序(集合具备比较性) 让集合的构造方法接收一个比较器接口的子类对象 Comparator */public class TreeSetDemo { public static void main(String[] args) { // 创建集合对象 // TreeSet<Student> ts = new TreeSet<Student>(); //自然排序 // public TreeSet(Comparator comparator) //比较器排序 // TreeSet<Student> ts = new TreeSet<Student>(new MyComparator()); // 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象 // 而匿名内部类就可以实现这个东西 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() { public int compare(Student s1, Student s2) { // 姓名长度 int num = s1.getName().length() - s2.getName().length(); // 姓名内容 int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; // 年龄 int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; return num3; } }); // 创建元素 Student s1 = new Student("linqingxia", 27); Student s2 = new Student("zhangguorong", 29); Student s3 = new Student("wanglihong", 23); Student s4 = new Student("linqingxia", 27); Student s5 = new Student("liushishi", 22); Student s6 = new Student("wuqilong", 40); Student s7 = new Student("fengqingy", 22); Student s8 = new Student("linqingxia", 29); // 添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); ts.add(s8); // 遍历 for (Student s : ts) { System.out.println(s.getName() + "---" + s.getAge()); } }}

运行结果:

3、集合的遍历

迭代:是取出集合中元素的一种方式。

而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都具有共性内容: 判断和取出。那么就可以将这些共性抽取。那么这些内部类都符合一个规则(或者说都抽取出来一个规则)。该规则就是Iterator。通过一个对外提供的方法:iterator();,来获取集合的取出对象。因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。

迭代的常见操作

PS:在迭代时循环中next调用一次,就要hasNext判断一次。

并发修改异常,原因:迭代器依赖于集合存在,修改集合元素而迭代器却不知道。

解决方法:

A:迭代器迭代元素,迭代器修改。因为Iterator没有添加功能,所以使用其子接口ListIterator,元素在迭代元素的后面添加。 B:集合遍历元素,集合修改元素(普通for和get(index)结合),元素在最后添加

package cn.itcast;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.ListIterator;/* * 问题:有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。 * * ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。 * 产生的原因: * 迭代器是依赖于集合而存在的,在判断成功后,集合的中新添加了元素,而迭代器却不知道,所以就报错了,这个错叫并发修改异常。 * 其实这个问题描述的是:迭代器遍历元素的时候,通过集合是不能修改元素的。 * 如何解决呢? * A:迭代器迭代元素,迭代器修改元素,元素是跟在刚才迭代的元素后面的。 * B:集合遍历元素,集合修改元素(普通for),元素在最后添加的。 */public class ListIteratorDemo { public static void main(String[] args) { // 创建List集合对象 List list = new ArrayList(); // 添加元素 list.add("hello"); list.add("world"); list.add("java"); // 迭代器遍历 // Iterator it = list.iterator(); // while (it.hasNext()) { // String s = (String) it.next(); // if ("world".equals(s)) { // list.add("javaee"); // } // } // 方式1:迭代器迭代元素,迭代器修改元素 // 而Iterator迭代器却没有添加功能,所以我们使用其子接口ListIterator ListIterator lit = list.listIterator(); while (lit.hasNext()) { String s = (String) lit.next(); if ("world".equals(s)) { lit.add("javaee"); } } // 方式2:集合遍历元素,集合修改元素(普通for) for (int x = 0; x < list.size(); x++) { String s = (String) list.get(x); if ("world".equals(s)) { list.add("javaee"); } } System.out.println("list:" + list); }}

Map接口概述

map

1、Map接口概述

将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值。其实Map集合中存储的就是键值对。map集合中必须保证键的唯一性。

Map接口和Collection接口的不同

Map是双列的,Collection是单列的Map的键唯一,Collection的子体系Set是唯一的Map集合的数据结构值针对键有效,跟值无关Collection集合的数据结构是针对元素有效

Map常用的子类:

Hashtable:内部结构是哈希表,是同步的。不允许null作为键,null作为值。Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合。HashMap:内部结构式哈希表,不是同步的。允许null作为键,null作为值。TreeMap:内部结构式二叉树,不是同步的。可以对Map结合中的键进行排序。HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。

2、Map集合遍历

方式1:根据键找值。获取所有键的集合,遍历键的集合,获取到每一个键,根据键找值。

package cn.itcast;import java.util.HashMap;import java.util.Map;import java.util.Set;/* * Map集合的遍历。 * Map -- 夫妻对 * 思路: * A:把所有的丈夫给集中起来。 * B:遍历丈夫的集合,获取得到每一个丈夫。 * C:让丈夫去找自己的妻子。 * * 转换: * A:获取所有的键 * B:遍历键的集合,获取得到每一个键 * C:根据键去找值 */public class MapDemo { public static void main(String[] args) { // 创建集合对象 Map<String, String> map = new HashMap<String, String>(); // 创建元素并添加到集合 map.put("杨过", "小龙女"); map.put("郭靖", "黄蓉"); map.put("杨康", "穆念慈"); map.put("陈玄风", "梅超风"); // 遍历 // 获取所有的键 Set<String> set = map.keySet(); // 遍历键的集合,获取得到每一个键 for (String key : set) { // 根据键去找值 String value = map.get(key); System.out.println(key + "---" + value); } }}

方式2:根据键值对对象找键和值。

获取所有键值对对象的集合遍历键值对对象的集合,获取到每一个键值对对象根据键值对对象找键和值package cn.itcast;import java.util.HashMap;import java.util.Map;import java.util.Set;/* * Map集合的遍历。 * Map -- 夫妻对 * * 思路: * A:获取所有结婚证的集合 * B:遍历结婚证的集合,得到每一个结婚证 * C:根据结婚证获取丈夫和妻子 * * 转换: * A:获取所有键值对对象的集合 * B:遍历键值对对象的集合,得到每一个键值对对象 * C:根据键值对对象获取键和值 * * 这里面最麻烦的就是键值对对象如何表示呢? * 看看我们开始的一个方法: * Set<Map.Entry<K,V>> entrySet():返回的是键值对对象的集合 */public class MapDemo { public static void main(String[] args) { // 创建集合对象 Map<String, String> map = new HashMap<String, String>(); // 创建元素并添加到集合 map.put("杨过", "小龙女"); map.put("郭靖", "黄蓉"); map.put("杨康", "穆念慈"); map.put("陈玄风", "梅超风"); // 获取所有键值对对象的集合 Set<Map.Entry<String, String>> set = map.entrySet(); // 遍历键值对对象的集合,得到每一个键值对对象 for (Map.Entry<String, String> me : set) { // 根据键值对对象获取键和值 String key = me.getKey(); String value = me.getValue(); System.out.println(key + "---" + value); } }}

3、HashMap类概述

键是哈希表结构,可以保证键的唯一性

4、LinkedHashMap类概述

Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。

5、TreeMap类概述

键是红黑树结构,可以保证键的排序和唯一性,自然排序,比较器排序。

6、Map集合的应用及扩展

package cn.itcast;import java.util.Scanner;import java.util.Set;import java.util.TreeMap;/* * 需求 :"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1) * * 分析: * A:定义一个字符串(可以改进为键盘录入) * B:定义一个TreeMap集合 * 键:Character * 值:Integer * C:把字符串转换为字符数组 * D:遍历字符数组,得到每一个字符 * E:拿刚才得到的字符作为键到集合中去找值,看返回值 * 是null:说明该键不存在,就把该字符作为键,1作为值存储 * 不是null:说明该键存在,就把值加1,然后重写存储该键和值 * F:定义字符串缓冲区变量 * G:遍历集合,得到键和值,进行按照要求拼接 * H:把字符串缓冲区转换为字符串输出 * * 录入:linqingxia * 结果:result:a(1)g(1)i(3)l(1)n(2)q(1)x(1) */public class TreeMapDemo { public static void main(String[] args) { // 定义一个字符串(可以改进为键盘录入) Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String line = sc.nextLine(); // 定义一个TreeMap集合 TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>(); // 把字符串转换为字符数组 char[] chs = line.toCharArray(); // 遍历字符数组,得到每一个字符 for (char ch : chs) { // 拿刚才得到的字符作为键到集合中去找值,看返回值 Integer i = tm.get(ch); // 是null:说明该键不存在,就把该字符作为键,1作为值存储 if (i == null) { tm.put(ch, 1); } else { // 不是null:说明该键存在,就把值加1,然后重写存储该键和值 i++; tm.put(ch, i); } } // 定义字符串缓冲区变量 StringBuilder sb = new StringBuilder(); // 遍历集合,得到键和值,进行按照要求拼接 Set<Character> set = tm.keySet(); for (Character key : set) { Integer value = tm.get(key); sb.append(key).append("(").append(value).append(")"); } // 把字符串缓冲区转换为字符串输出 String result = sb.toString(); System.out.println("result:" + result); }}

示例2:在很多项目中,应用比较多的是一对多的映射关系,这就可以通过嵌套的形式将多个映射定义到一个大的集合中,并将大的集合分级处理,形成一个体系。

package cn.itcast;import java.util.ArrayList;import java.util.HashMap;import java.util.Set;class Student { private String name; private int age; public Student() { super(); } public Student(String name, int age) { super(); this.name = name; this.age = age; } 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; }}/* * 黑马程序员 * bj 北京校区 * jc 基础班 * 林青霞 27 * 风清扬 30 * jy 就业班 * 赵雅芝 28 * 武鑫 29 * sh 上海校区 * jc 基础班 * 郭美美 20 * 犀利哥 22 * jy 就业班 * 罗玉凤 21 * 马征 23 * gz 广州校区 * jc 基础班 * 王力宏 30 * 李静磊 32 * jy 就业班 * 郎朗 31 * 柳岩 33 * xa 西安校区 * jc 基础班 * 范冰冰 27 * 刘意 30 * jy 就业班 * 李冰冰 28 * 张志豪 29 */public class HashMapDemo { public static void main(String[] args) { // 创建大集合 HashMap<String, HashMap<String, ArrayList<Student>>> czbkMap = new HashMap<String, HashMap<String, ArrayList<Student>>>(); // 北京校区数据 HashMap<String, ArrayList<Student>> bjCzbkMap = new HashMap<String, ArrayList<Student>>(); ArrayList<Student> array1 = new ArrayList<Student>(); Student s1 = new Student("林青霞", 27); Student s2 = new Student("风清扬", 30); array1.add(s1); array1.add(s2); ArrayList<Student> array2 = new ArrayList<Student>(); Student s3 = new Student("赵雅芝", 28); Student s4 = new Student("武鑫", 29); array2.add(s3); array2.add(s4); bjCzbkMap.put("基础班", array1); bjCzbkMap.put("就业班", array2); czbkMap.put("北京校区", bjCzbkMap); // 西安校区数据 HashMap<String, ArrayList<Student>> xaCzbkMap = new HashMap<String, ArrayList<Student>>(); ArrayList<Student> array3 = new ArrayList<Student>(); Student s5 = new Student("范冰冰", 27); Student s6 = new Student("刘意", 30); array3.add(s5); array3.add(s6); ArrayList<Student> array4 = new ArrayList<Student>(); Student s7 = new Student("李冰冰", 28); Student s8 = new Student("张志豪", 29); array4.add(s7); array4.add(s8); xaCzbkMap.put("基础班", array3); xaCzbkMap.put("就业班", array4); czbkMap.put("西安校区", xaCzbkMap); // 遍历集合 Set<String> czbkMapSet = czbkMap.keySet(); for (String czbkMapKey : czbkMapSet) { System.out.println(czbkMapKey); HashMap<String, ArrayList<Student>> czbkMapValue = czbkMap .get(czbkMapKey); Set<String> czbkMapValueSet = czbkMapValue.keySet(); for (String czbkMapValueKey : czbkMapValueSet) { System.out.println("/t" + czbkMapValueKey); ArrayList<Student> czbkMapValueValue = czbkMapValue .get(czbkMapValueKey); for (Student s : czbkMapValueValue) { System.out.println("/t/t" + s.getName() + "---" + s.getAge()); } } } }}

运行结果:

集合框架的综合应用

代码示例:模拟斗地主洗牌和发牌

package cn.itcast_04;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.TreeSet;/* * 思路: * A:创建一个HashMap集合 * B:创建一个ArrayList集合 * C:创建花色数组和点数数组 * D:从0开始往HashMap里面存储编号,并存储对应的牌 * 同时往ArrayList里面存储编号即可。 * E:洗牌(洗的是编号) * F:发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收) * G:看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌) */public class PokerDemo { public static void main(String[] args) { // 创建一个HashMap集合 HashMap<Integer, String> hm = new HashMap<Integer, String>(); // 创建一个ArrayList集合 ArrayList<Integer> array = new ArrayList<Integer>(); // 创建花色数组和点数数组 // 定义一个花色数组 String[] colors = { "♠", "♥", "♣", "♦" }; // 定义一个点数数组 String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2", }; // 从0开始往HashMap里面存储编号,并存储对应的牌,同时往ArrayList里面存储编号即可。 int index = 0; for (String number : numbers) { for (String color : colors) { String poker = color.concat(number); hm.put(index, poker); array.add(index); index++; } } hm.put(index, "小王"); array.add(index); index++; hm.put(index, "大王"); array.add(index); // 洗牌(洗的是编号) Collections.shuffle(array); // 发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收) TreeSet<Integer> fengQingYang = new TreeSet<Integer>(); TreeSet<Integer> linQingXia = new TreeSet<Integer>(); TreeSet<Integer> liuYi = new TreeSet<Integer>(); TreeSet<Integer> dipai = new TreeSet<Integer>(); for (int x = 0; x < array.size(); x++) { if (x >= array.size() - 3) { diPai.add(array.get(x)); } else if (x % 3 == 0) { fengQingYang.add(array.get(x)); } else if (x % 3 == 1) { linQingXia.add(array.get(x)); } else if (x % 3 == 2) { liuYi.add(array.get(x)); } } // 看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌) lookPoker("风清扬", fengQingYang, hm); lookPoker("林青霞", linQingXia, hm); lookPoker("刘意", liuYi, hm); lookPoker("底牌", diPai, hm); } // 写看牌的功能 public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) { System.out.print(name + "的牌是:"); for (Integer key : ts) { String value = hm.get(key); System.out.print(value + " "); } System.out.println(); }}

运行结果:


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表