第十一章、集合
用于存储一种或者多种引用数据类型,并且长度可变的容器。
集合的外延有:List集合、Set集合、Map集合....
数组和集合的比较
1.数组的特点
a. 数组本质上就是一段连续的内存空间,用于记录多个类型相同的数据
b. 数组一旦声明完毕,则内存空间固定不变。
c. 插入和删除操作的时候不方便,可能会移动大量的元素导致效率太低
d. 支持下标访问,可以实现随机访问。
e. 数组中的元素可以是基本数据类型,也可以是引用数据类型。
2. 集合的特点
a. 内存空间可以不连续,数据类型可以不相同
b. 集合的内容空间可以动态调整
c. 集合的插入删除操作可以不移动大量元素
d. 部分支持下标访问,部分不支持
e. 集合中的元素必须是引用数据类型
1. List集合接口
是用于有序存储可重复元素的集合。
List接口继承了Collection接口,声明有序存储对象(可重复)功能的公共接口。
List集合的实现类有:ArrayList、Vector、LinkedList
集合中只能存储引用数据类型,不能存储基本数据类型
List集合常用方法:
add(E e) - 向列表末尾追加元素
add(int index,E e) - 在指定位置上添加一个对象
addAll(Collection<? extends E> c) - 将集合元素添加到指定集合的末尾
get(int index) 返回指定位置的对象
remove(int index) 删除指定位置的对象
set(int index, E element) - 用指定元素替换列表中指定位置的元素(可选操作)。
indexOf(Object o) - 返回第一个匹配对象的位置
lastIndexOf(Object o) - 返回最后一个匹配对象的索引
size() - 返回此列表中的元素数。
1.1 ArrayList类
数组实现,查找快,增删慢,由于是数组实现,在增加和删除的时候会牵扯到数组扩容以及拷贝元素,所以慢。数组是可以直接按索引查找,所以在查找的时候较为快。
-
实例代码
package demo1; import java.util.ArrayList; import java.util.List; // 演示集合常用类 public class TestArrayList { public static void main(String[] args) { // 获取List/Collection集合的应用那个 // List list = new List(); // error 接口不能构造对象 // 接口类型的引用指向实现类的对象,形成了多态 List list1 = new ArrayList(); // 向集合list1中添加元素 boolean b1 = list1.add(new Integer(123)); System.out.println("b1 = "+b1); System.out.println(list1); // [123] // 自动调用toString(),编译器自动添加 // 在编译阶段调用父类的方法,在运行阶段调用子类重写以后的方法,ArrayList类 // toString()默认打印的格式为:[元素1,元素2,...] b1 = list1.add(new String("zhangsan")); System.out.println("b1 = "+b1); System.out.println(list1); // [123, zhangsan] // 添加自定义对象 b1 = list1.add(new Student(1001, "lisi", 18)); System.out.println("b1 = "+b1); System.out.println(list1); System.out.println("-----------------------"); // addAll() 将指定集合中的元素添加到调用该方法的集合中(将集合直接添加到集合中) // 准备另一个集合 List list2 = new ArrayList(); list2.add(456); // 采用了自动装箱技术 int=》Integer System.out.println("list2 = "+list2); list2.add("wangwu"); System.out.println("list2 = "+list2); System.out.println("list1中的元素个数是:"+list1.size()); System.out.println("list2中的元素个数是:"+list2.size()); System.out.println("-----------------------"); // 将集合list2中所有的元素添加到集合list1中 b1 = list1.addAll(list2); System.out.println("b1 = "+b1); System.out.println("list1 = "+list1); System.out.println("list2 = "+list2); System.out.println("-----------------------"); // contains(Object o) 判断集合中是否包含指定的单个元素 // containsAll(Collection<?> c) 表示判断中是否包含指定元素的整体 // 判断集合list1中是否包含参数指定的单个元素 b1 = list1.contains(1234); System.out.println("b1 = "+b1); b1 = list1.contains(456); System.out.println("b1 = "+b1); b1 = list1.contains(new String("zhangsan")); System.out.println("b1 = "+b1); // contains方法原理:参数对象调用equals()方法与集合中的元素一个一个比较 // 当Student类没有重写equals()方法的时候,则比较的是地址,因此返回结果是false // 当Student类重写equals()方法时,则比较对象的内容,返回结果true b1 = list1.contains(new Student(1001,"lisi",18)); System.out.println("b1 = "+b1); System.out.println("---------------------"); // 将集合list2中的元素一个一个拿出来与list1进行比较 b1 = list1.containsAll(list2); System.out.println("b1 = "+b1); // true // 判断集合list1中是否包含list2这个整体 b1 = list1.contains(list2); System.out.println("b1 = "+b1); // false System.out.println("------------------"); /* * remove(int index) 移除列表中指定位置的元素 * removeAll(Collection<?> c) 从列表中移除指定collection中包含的其所有元素 */ // 实现集合list1中单个元素的删除 b1 = list1.remove(new String("1")); System.out.println("b1 = "+b1); b1 = list1.remove(new String("zhangsan")); System.out.println("b1 = "+b1); System.out.println("list1 = "+list1); System.out.println("------------------"); // 实现集合list1中多个元素的删除 // 表示从集合list1中将结合list2中的元素一个一个删除 b1 = list1.removeAll(list2); System.out.println("b1 = "+b1); System.out.println("list1 = "+list1); b1 = list1.remove(list2); System.out.println("b1 = "+b1); System.out.println("list1 = "+list1); System.out.println("------------------"); // 实现集合list1中所有元素怒的删除 /*list1.clear(); System.out.println("list1 = "+list1); System.out.println(list1.isEmpty()); System.out.println(list1.size()); System.out.println("list1 = "+list1); b1 = list1.retainAll(list2); System.out.println("b1 = "+b1);*/ System.out.println("------------------"); // set(int index,E element) System.out.println("list1 = "+list1); list1.set(0, "你好"); System.out.println("list1 = "+list1); System.out.println("------------------"); // 将集合转换为数组 Object[] array = list1.toArray(); System.out.println("array = "+array); System.out.println("------------------"); System.out.println("list1 = "+list1); // 计算list1和list2的交集并保留到list1中,若list1集合中内容发生改变就返回true b1 = list1.retainAll(list2); System.out.println("b1 = "+b1); System.out.println("list1 = "+list1); System.out.println("list2 = "+list2); System.out.println("-------------------"); System.out.println(list2); // 计算两个集合的交集并保留到当前集合中 // 计算list2和list2的交集并保留到list2中,list2中内容不发生改变,因此返回false b1 = list2.retainAll(list2); System.out.println("b1 = "+b1); // false System.out.println("list2 = "+list2); System.out.println("=========================="); List l1 = new ArrayList(); List l2 = new ArrayList(); l1.add(1); l1.add(2); l1.add(3); l1.add("jason"); l2.add(1); l2.add("jason"); System.out.println("l1 = "+l1); System.out.println("l2 = "+l2); System.out.println("------------------"); boolean b = l1.retainAll(l2); System.out.println("b = "+b); System.out.println("l1 = "+l1); System.out.println("l2 = "+l2); } }
1.2 LinkedList类
链表实现,增删快、查找慢
由于链表实现,增加时候只要让前一个元素记住自己就可以,删除时候让前一个元素记住后一个元素,后一个元素记录前一个元素这样大的增删效率比较高,但查询时需要一个一个的遍历,所以效率较低
-
实例代码
package demo2; public class Dog { private String name; private String type; public Dog() { super(); // TODO Auto-generated constructor stub } public Dog(String name, String type) { super(); this.name = name; this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return "Dog [name=" + name + ", type=" + type + "]"; } }
package demo2; import java.util.LinkedList; /* * 存储多条狗狗的信息,获取第一条和最后一条的信息,并打印狗狗信息 * 通过List接口实现类LinkedList是吸纳该需求 * 获取第一个元素 * 获取第二个元素 * 删除狗狗信息 */ public class TestLinkedList { public static void main(String[] args) { LinkedList<Dog> list = new LinkedList<>(); Dog dog1 = new Dog("jason", "田园犬"); Dog dog2 = new Dog("alex", "拉布拉多"); Dog dog3 = new Dog("tank", "藏獒"); list.add(dog1); list.add(dog2); list.add(dog3); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } Dog dog11 = list.getFirst(); System.out.println("第一只狗:"+dog11); Dog dog22 = list.get(1); System.out.println("第二只狗:"+dog22); Dog dog33 = list.removeFirst(); System.out.println("被删除的狗是:"+dog33); Dog dog44 = list.removeLast(); System.out.println("被删除的最后一条狗是:"+dog44); } }
2. Set集合接口
Set集合是用于无序存储不可重复的元素集合。
Set集合的实现类:
HashSet
LinkedHashSet
TreeSet
2.1 HashSet类
HashSet是基于哈希表的Set集合
1.需要使用hashCode算法计算元素的hash值
2.基于哈希表做实现
3.实现了Set接口
HashSet集合存储步骤:
1.使用哈希算法计算元素对应的哈希值,使用此哈希值作为地址存储
2.判断此哈希值对应的位置上是否已经存有元素
3.若没有就将元素存储在该位置上
4.若有则使用equals方法判断两个对象是否相等,相等就不存储,不相等则与上一个元素存在一起
-
实例代码
package demo1; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /* * HashSet使用 */ public class HashSetTest { public static void main(String[] args) { // 1.声明一个Set集合 Set<String> set = new HashSet<String>(); // 2.向集合中添加车票信息 set.add("北京------河北"); set.add("云南------贵州"); set.add("江苏------河南"); set.add("云南------贵州"); System.out.println("--------第一种遍历:增强for循环--------"); for (String string : set) { System.out.println(string); } System.out.println("--------第二种遍历:迭代器循环--------"); Iterator<String> iterator = set.iterator(); while(iterator.hasNext()){ String next = iterator.next(); System.out.println(next); } System.out.println("--------第三种遍历:toString遍历--------"); System.out.println(set); System.out.println("--------第四种遍历:普通for循环--------"); // HashSet集合是无序的,因为没有下标,所以不能通过下标取值 for (int i = 0; i < set.size(); i++) { } // 此处是将HashSet集合转换成数组再遍历输出 /*Object[] array = set.toArray(); for (int i = 0; i < array.length; i++) { System.out.println(array[i]); }*/ } }
2.2 LinkedHashSet类
定义:是基于双向链表和哈希表、继承自HashSet的Set集合。
特点:有序、不可重复
-
实例代码
package demo1; import java.util.LinkedHashSet; import java.util.Set; /* * 演示LinkedHashSet */ public class LinkedHashSetTest { public static void main(String[] args) { // 1. 创建Set集合限定存储元素的类型为String类型 Set<String> set = new LinkedHashSet<>(); set.add("绝地求生"); set.add("王者荣耀"); set.add("球球大作战"); set.add("绝地求生"); for (String string : set) { System.out.println(string); } } }
2.3 TreeSet类
是基于红黑树,实现了Set集合,具有排序功能的Set集合。
-
实例代码
package demo1; import java.util.TreeSet; /* * 实现使用TreeSet集合进行存储数据并遍历 * 创建TreeSet集合对象,次昂其中存储180,169,157,169,185五个学生的身高,遍历输出查询结果 */ public class TestTreeSet { public static void main(String[] args) { // 创建TreeSet集合 TreeSet<Integer> set = new TreeSet<>(); // 添加元素 set.add(180); set.add(169); set.add(157); set.add(169); set.add(185); for (Integer integer : set) { System.out.println(integer); } } }
3. Mp集合接口
思考一个问题:
如何根据地图中位置找到对应公司的信息
键-----映射-----值
Map集合存储元素的特点是以键值对形式存储元素,是一个容器框架
实现类:
HashMap
LinkedHashMap
TreeMap
Hashtable
3.1 HashMap类
定义:基于哈希表试下的,线程不安全的Map容器
特点:
1.基于哈希表实现
2.key不允许重复,值可以重复
3.底层是数组+链表实现的
4.允许null键和null值
5.线程不安全
HashMap常用方法
V put(K key,V value) 添加数据,如果先前包含该键的映射,则替换旧值
V get(Object key) 返回指定键所映射的值
Set<Map.Entry<K,V>> entrySet() 返回此地址中包含的的映射的Set集合
Set<K> keySet() 返回集合中包含的键的Set集合
Collection<V> values() 返回该集合中包含的值的Collection集合
V remove(Object key) 从该集合中删除指定键的映射
-
示例代码
package demo1; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /* * 测试HashMap方法 */ public class HashMapDemo { public static void main(String[] args) { // 1.创建HashMap对象 Map<String, String> map = new HashMap(); // 2.向HashMap对象中添加元素 map.put("杭州", "阿里"); map.put("深圳", "腾讯"); map.put("北京", "百度"); map.put("深圳", "腾讯新"); map.put(null, null); // HashMap允许null键和null值的 // 3.遍历输出HashMap里面的元素 System.out.println("-----------通过values()方法获取values值-------------"); Collection<String> values = map.values(); for (String string : values) { System.out.println(string); } System.out.println("-----------通过keySet()获取Map集合中所有的key值-------------"); Set<String> set = map.keySet(); for (String string : set) { // Map中提供一个get(key)方法,可以通过制定的key来获取对应的value值 String value = map.get(string); System.out.println(string+" "+value); } System.out.println("-----------通过entrySet()获取Map集合中所有的key和value值-------------"); // entrySet()里面封装了map中的key和value值 Set<Entry<String,String>> entrySet = map.entrySet(); for (Entry<String, String> entry : entrySet) { String key = entry.getKey(); // 获取key String value = entry.getValue(); // 获取value System.out.println(key+" "+value); } } }
3.2 LinkedHashMap类
定义:继承自HashMap,基于双向链表存储有序的Map容器
package demo1;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/*
* 演示LinkedHashMap使用
* 存储有序的集合
*/
public class LinkedHashMapDemo {
public static void main(String[] args) {
Map<String, Integer> map = new LinkedHashMap<>();
map.put("jason", 18);
map.put("tank", 32);
map.put("wusir", 12);
Set<String> set = map.keySet();
for (String string : set) {
Integer value = map.get(string);
System.out.println(string+" "+value);
}
}
}
3.3 Hashtable类
定义:基于散列表实现,线程安全的Map容器
特定:
1.不允许null键和null值
2.线程安全
HashMap与Hashtable的区别
1.继承的父类不同
2.线程安全性不同
3.是否允许null值
4.遍历内容实现不同
遍历集合都可以使用Iterator
Hashtable还可以获取Enumberation
-
实例代码
package demo1; import java.util.Hashtable; import java.util.Map; import java.util.Set; /* * 演示Hashtable的使用 */ public class HashtableDemo { public static void main(String[] args) { // 1.创建Map集合 Map<String, String> map = new Hashtable<>(); // 2.添加元素 map.put("zhangsan", "java"); map.put("lisi", "android"); map.put("wangwu", "python"); map.put("zhaoliu", "c++"); // Hashtable 的键不能为null,值也不能为null // 3.遍历集合 Set<String> set = map.keySet(); for (String string : set) { System.out.println(string+" "+map.get(string)); } } }
3.4 TreeMap类
定义:基于红黑树实现,具有排序功能的Map容器
特点:
1.底层是红黑数
2.不允许null值和null键
3.针对键排序
4.线程不安全
4. Queue 队列
定义:队列先进先出
add()和remove在失败的时候会抛出异常(不推荐)
常用方法:
1. offer() 添加元素
2. poll() 返回第一个元素,并在队列中删除
3. element() 返回第一个元素
4. peek() 返回第一个元素
-
实例代码
package demo3; import java.util.LinkedList; import java.util.Queue; public class TestQueue { public static void main(String[] args) { // 创建队列集合 Queue q = new LinkedList<>(); // offer 插入 q.add("你好哇"); q.add("好什么好!"); q.offer("别逼逼,就逼话多"); // 获取头部元素但不删除 Object peek = q.peek(); System.out.println(peek); // 获取头部元素但不删除 Object poll = q.poll(); System.out.println(poll); System.out.println("-----------"); for (Object object : q) { System.out.println(object); } } }
5. 集合工具类
常用方法:
void sort() 对集合中存储的元素进行排序
shuffle() 对集合中存储的元素进行随机排序
reverse() 将集合中存储的元素位置进行反转
max() 获取集合中存储的元素的最大元素
min() 获取集合中存储的元素的最小元素
binarySearch() 二分查找
indexOfSubList() 获取指定字列表在整个列表中第一次出现的位置
lastIndexOfSubList() 获取指定字列表在整个列表中最后一次出现的位置
6. 集合的选择
6.1 List
- 如果需要保留存储顺序和重复元素,推荐使用List集合
- ArrayList:若查询加多,推荐使用
- LinkedList:若存取加多,推荐使用
- Vector:若需要线程安全,推荐使用
6.2 Set
- 若果不需要保留存储顺序并且需要去除重复的元素,推荐使用Set集合
- TreeSet:若需要将元素进行排序,推荐使用
- HashSet:若不需要排序,推荐使用,比TreeSet效率高
- LinkedHashSet:若选择保留存储顺序,有需要去除重复的元素,推荐使用
7. 遍历集合的方法
7.1 迭代器遍历
迭代器(Iterator)
迭代器是一种使用迭代法遍历集合的对象
Iterable集合是java集合框架的顶级集合
Iterator接口中常用的方法:
hasNext() - 判断是否还有其他元素
next() - 获取下一个元素
remove() - 删除最后一次调用next方法返回的元素
遍历步骤:
1. 调用Iterator()得到一个指向集合序列第一个元素的迭代器
2. 用循环调用hasNext()方法,如果有元素,返回true
3. 在循环中,使用next()方法获取集合中的下一个元素
7.2 增强for遍历
语法格式:
for(元素类型 e:数组名/Iterable实例){
}
增强for和普通for有什么区别?
增强for的内部也就是调用iterator实现的,但是增强for循环有一小点缺陷,比如说不能再
增强for循环中动态的删除集合内容,不能获取下标等。
7.3 普通for遍历
语法格式:
for(循环控制器;循环条件;循环控制器变化条件){
循环主体
}
7.4 toString遍历
System.out.println(集合名);
8. 排序接口
Comparable接口
使实现类自身具备某种比较规则以便对其对象进行自然排序的接口。
自然排序:是要求自自定义类实现Comparable接口并重写其compareTo(T o) 方法,在此方法中依据xx属性进行排序的算法。
Comparator比较器
在外部自定义比较规则以便容器对其存储数据进行定制排序的对象。
定制排序:是要求自定义类实现Comparator接口并重写其compare(T o1, T o2) 方法,在此方法中依据xx属性
进行排序的算法,也称为比较器排序。
Comparable接口
实现该接口的类具有排序功能
需要重写compareTo方法,使用当前对象和参数对象进行比较
Comparator接口
不会对集合中存储的元素进行修饰
需要自定义比较器类实现该接口,重写compare方法,比较两个参数对象
需要将比较器对象以参数形式传入集合的构造器中
-
实例代码
// 实现类 package demo1; import java.util.Comparator; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; /* * 实现TreeMap的排序功能 */ public class TestStudent { public static void main(String[] args) { // 1.创建Map集合 // 添加比较器方式一: Map<Student, String> map = new TreeMap<>(new Sort()); // 添加比较器方式二: /*Map<Student, String> map = new TreeMap<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getAge() - o2.getAge(); } });*/ // 2.向Map集合中添加元素 map.put(new Student("jason", 18, "read"), "1001"); map.put(new Student("tank", 20, "dance"), "1002"); map.put(new Student("wusir", 12, "swim"), "1003"); // 3.遍历输出 Set<Entry<Student,String>> entrySet = map.entrySet(); Iterator<Entry<Student, String>> iterator = entrySet.iterator(); while(iterator.hasNext()){ Entry<Student, String> next = iterator.next(); Student key = next.getKey(); String value = next.getValue(); System.out.println(key+" "+value); } } }
// 学生类 package demo1; public class Student /*implements Comparable<Student>*/{ private String name; private int age; private String hobby; public Student() { super(); } public Student(String name, int age, String hobby) { super(); this.name = name; this.age = age; this.hobby = hobby; } 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 getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", hobby=" + hobby + "]"; } /*@Override public int compareTo(Student o) { return this.age - o.age; }*/ }
// Comparator比较器 package demo1; import java.util.Comparator; public class Sort implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.getAge() - o2.getAge(); } }