Java 集合
Collection 集合
1.1 集合概述
集合:集合是 java 中提供的一种容器,可以用来存储多个数据。
1.2 集合框架
JAVASE 提供了满足各种需求的 API,在使用这些 API 前,先了解其继承与接口操作架构,才
能了解何时采用哪个类,以及类之间如何彼此合作,从而达到灵活应用。
集合按照其存储结构可以分为两大类,分别是单列集合 java.util.Collection 和双列集合
java.util.Map
Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的
子接口,分别是 java.util.List 和 java.util.Set。其中,List 的特点是元素有序、元素
可 重 复 。 Set 的 特 点 是 元 素 无 序 , 而 且 不 可 重 复 。 List 接 口 的 主 要 实 现 类 有
java.util.ArrayList 和 java.util.LinkedList , Set 接 口 的 主 要 实 现 类 有
java.util.HashSet 和 java.util.TreeSet。
1.3 Collection 常用功能
Collection 是所有单列集合的父接口,因此在 Collection 中定义了单列集合(List 和 Set)
通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(E e)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中。
方法演示:
public class Demo01Collection {
public static void main(String[] args) {
//创建集合对象,可以使用多态
//Collection coll = new ArrayList<>();
Collection coll = new HashSet<>();
System.out.println(coll);//重写了 toString 方法 []
/*
public boolean add(E e): 把给定的对象添加到当前集合中 。
返回值是一个 boolean 值,一般都返回 true,所以可以不用接收
/
boolean b1 = coll.add(“张三”);
System.out.println(“b1:”+b1);//b1:true
System.out.println(coll);//[张三]
coll.add(“李四”);
coll.add(“李四”);
coll.add(“赵六”);
coll.add(“田七”);
System.out.println(coll);//[张三, 李四, 赵六, 田七]
/
public boolean remove(E e): 把给定的对象在当前集合中删除。
返回值是一个 boolean 值,集合中存在元素,删除元素,返回 true
集合中不存在元素,删除失败,返回 false
/
boolean b2 = coll.remove(“赵六”);
System.out.println(“b2:”+b2);//b2:true
boolean b3 = coll.remove(“赵四”);
System.out.println(“b3:”+b3);//b3:false
System.out.println(coll);//[张三, 李四, 田七]
/
public boolean contains(E e): 判断当前集合中是否包含给定的对象。
包含返回 true
不包含返回 false
*/
boolean b4 = coll.contains(“李四”);
System.out.println(“b4:”+b4);//b4:true
boolean b5 = coll.contains(“赵四”);
System.out.println(“b5:”+b5);//b5:false
//public boolean isEmpty(): 判断当前集合是否为空。 集合为空返回 true,集
合不为空返回 false
boolean b6 = coll.isEmpty();
System.out.println(“b6:”+b6);//b6:false
//public int size(): 返回集合中元素的个数。
int size = coll.size();
System.out.println(“size:”+size);//size:3
//public Object[] toArray(): 把集合中的元素,存储到数组中。
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//public void clear() :清空集合中所有的元素。但是不删除集合,集合还存在
coll.clear();
System.out.println(coll);//[]
System.out.println(coll.isEmpty());//true
}
}
Iterator 迭代器
2.1 Iterator 接口
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK 专门提供了一个接口
java.util.Iterator。Iterator 接口也是 Java 集合中的一员,但它与 Collection、Map 接
口有所不同,Collection 接口与 Map 接口主要用于存储元素,而 Iterator 主要用于迭代访
问(即遍历)Collection 中的元素,因此 Iterator 对象也被称为迭代器。
想要遍历 Collection 集合,那么就要获取该集合迭代器完成迭代操作。获取迭代器的方法:public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的。
迭代的概念:
迭代:即 Collection 集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,
如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有
元素全部取出。这种取出方式专业术语称为迭代。
Iterator 接口的常用方法如下:public E next()
:返回迭代的下一个元素。public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。
案例:
public class IteratorDemo {
public static void main(String[] args){
ArrayList arr = new ArrayList();
//往集合里面去添加元素
arr.add(“111”);
arr.add(“222”);
arr.add(“333”);
//是使用集合里面的一个 iterator()来获取迭代器里面的对象,iterator 接口来
接收
//注意的地方;Iterator 接口是泛型的,迭代器的泛型是跟着集合走的
Iterator iter = arr.iterator();
while (iter.hasNext()){
String a = iter.next();
System.out.println(a);
}
// System.out.println(arr);
// Iterator iter = arr.iterator();
// boolean a = iter.hasNext();
// String b = iter.next();
// System.out.println(b);
//
// boolean c = iter.hasNext();
// System.out.println©;
// String d = iter.next();
// System.out.println(d);
//
// boolean e = iter.hasNext();
// System.out.println(e);
// String f = iter.next();
// System.out.println(f);
//
// boolean g = iter.hasNext();
// System.out.println(e);
// String h = iter.next();//NoSuchElementException
// System.out.println(f);
}
}
tips::在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的 next
方法,将会发生 java.util.NoSuchElementException 没有集合元素的错误。
2.2 迭代器的实现原理
当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()
方法判断集合中是否存在下一个元素,如果存在,则调用 next()方法将元素取出,否则说
明已到达了集合末尾,停止遍历元素。
Iterator 迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。
在调用 Iterator 的 next 方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,
当第一次调用迭代器的 next 方法后,迭代器的索引会向后移动一位,指向第一个元素并将
该元素返回,当再次调用 next 方法时,迭代器的索引会指向第二个元素并将该元素返回,
依此类推,直到 hasNext 方法返回 false,表示到达了集合的末尾,终止对元素的遍历。
2.3 增强 for 循环
增强 for 循环(也称 for each 循环)是JDK1.5以后出来的一个高级 for 循环,专门用来
遍历数组和集合的。它的内部原理其实是个 Iterator 迭代器,所以在遍历的过程中,不能
对集合中的元素进行增删操作。
格式:
for(元素的数据类型 变量 : Collection 集合 or 数组){
//写操作代码
}
它用于遍历 Collection 和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进
行增删操作。
tips: 新 for 循环必须有被遍历的目标。目标只能是 Collection 或者是数组。新式 for
仅仅作为遍历操作出现。
List 接口介绍
java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将
实现了 List 接口的对象称为 List 集合。在 List 集合中允许出现重复的元素,所有的元素
是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List
集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List 接口特点:
- 它是一个元素存取有序的集合。例如,存元素的顺序是 11、22、33。那么集合中,元素
的存储就是按照 11、22、33 的顺序完成的)。 - 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是
一个道理)。 - 集合中可以有重复的元素,通过元素的 equals 方法,来比较是否为重复的元素。
3.2 List 接口中常用方法
1.添加
add(E e)
addAll(Collection<? extends E> c)
2.删除
clear
remove(Object o)
removeAll(Collection<?> c)
retainAll(Collection<?> c)
3.判断
contains(Object o)
containsAll(Collection<?> c)
equals(Object o)
isEmpty()
4 获取
hashCode()
iterator()
size()
3.3 List 特有方法
List 作为 Collection 集合的子接口,继承了 Collection 接口中的全部方法,而且还增加
了一些根据元素索引来操作集合的特有方法,如下:
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位
置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值
的更新前的元素。
List 集合特有的方法都是跟索引相关:
public class ListDemo {
public static void main(String[] args) {
// 创建 List 集合对象
List list = new ArrayList();
// 往 尾部添加 指定元素
list.add(“11”);
list.add(“22”);
list.add(“33”);
System.out.println(list);
// add(int index,String s) 往指定位置添加
list.add(1,“99”);
System.out.println(list);
// String remove(int index) 删除指定位置元素 返回被删除元素
// 删除索引位置为 2 的元素
System.out.println(“删除索引位置为 2 的元素”);
System.out.println(list.remove(2));
System.out.println(list);
// String set(int index,String s)
// 在指定位置 进行 元素替代(改)
// 修改指定位置元素
list.set(0, “88”);
System.out.println(list);
// String get(int index) 获取指定位置元素
// 跟 size() 方法一起用 来 遍历的
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
//还可以使用增强 for
for (String string : list) {
System.out.println(string);
}
}
}
ArrayList 集合
1.底层是数组实现的
2.不是同步的
3.查询比较快
java.util.ArrayList 集合数据存储的结构是数组结构。方便元素添加、删除的集合。
方法演示:
public class ArrayListDemo {
public static void main(String[] args) {
//创建集合
ArrayList arr = new ArrayList<>();
//添加元素
// public boolean add(Object Obj){};
// arr.add(new Person(“zhansan”,18));
// arr.add(new Person(“lisi”,19));
// arr.add(new Person(“wangwu”,20));
//遍历集合中的元素
Iterator iter = arr.iterator();
//
while (iter.hasNext()){
System.out.println(iter.next().getName()+iter.next().getAge());
}
//自动装箱
// arr.add(8);//int,–>intger
// arr.add(new Integer(8));
//
// System.out.println(arr);
}
}
Person 类:
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
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;
}
}
源码解析:
当第一次调用 add()方法添加元素之后,长度变为 10.
当数组首次扩容的 10 个空间用完需要扩容后,会第二次走 grow 方法来扩容(每次扩容为
1.5 倍)
ArrayList 初始大小为 10,每次 1.5 倍进行扩容;它的底层是用数组实现的,所以查询速度
相对 LinkedList 要快。
在 jdk1.6 里面
ArrayList 初始化大小是 10 (如果你知道你的 arrayList 会达到多少容量,可以在初始
化的时候就指定,能节省扩容的性能开支)
扩容点规则是,新增的时候发现容量不够用了,就去扩容
扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 + 1。(例如:原始大小是 10 ,扩
容后的大小就是 10 + 5+1 = 16)
在 jdk1.7 里面
扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 。(例如:原始大小是 10 ,扩容
后的大小就是 10 + 5 = 15, 15+15/2 = 22, 22+22/2 = 33)
LinkedList
LinkedList 是一个继承于 AbstractSequentialList 的双向链表。它也可以被当作堆栈、
队列或双端队列进行操作
LinkedList 实现 List 接口,能对它进行队列操作
由于它的底层是用双向链表实现的,所以它对元素的增加、删除效率要比ArrayList 好;它是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。
LinkedList 底层的数据结构是基于双向循环链表的,且头结点中不存放数据,如下
Map 集合
概述
现实生活中,我们常会看到这样的一种集合:IP 地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java 提供了专门的集合类用来存放这种对象关系的对象,即 java.util.Map 接口。
通过查看 Map 接口描述,发现 Map 接口下的集合与 Collection 接口下的集合,它们存储数据的形式不同。
Collection
中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。Map
中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。Collection
中的集合称为单列集合,Map
中的集合称为双列集合。- 需要注意的是,
Map
中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
Map 常用子类
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的 hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap 下有个子类 LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的 hashCode()方法、equals()方法。
tips:Map 接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据
类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。
Map 接口中的常用方法
Map 接口中定义了很多方法,常用的如下:
public V put(K key, V value)
: 把指定的键与指定的值添加到 Map 集合中。public V remove(Object key)
: 把指定的键 所对应的键值对元素 在 Map 集合中删除,返回被删除元素的值。public V get(Object key)
根据指定的键,在 Map 集合中获取对应的值。boolean containsKey(Object key)
判断集合中是否包含指定的键。public Set<K> keySet()
: 获取 Map 集合中所有的键,存储到 Set 集合中。public Set<Map.Entry<K,V>> entrySet()
: 获取到 Map 集合中所有的键值对对象的集
合(Set 集合)。
Map 接口的方法演示
public class MapDemo {
public static void main(String[] args) {
//创建 map 对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素到集合
map.put(“黄晓明”, “杨颖”);
map.put(“文章”, “马伊琍”);
map.put(“邓超”, “孙俪”);
System.out.println(map);
//String remove(String key)
System.out.println(map.remove(“邓超”));
System.out.println(map);
// 想要查看 黄晓明的媳妇 是谁
System.out.println(map.get(“黄晓明”));
System.out.println(map.get(“邓超”));
}
}
tips:
使用 put 方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回 null,
并把指定的键值添加到集合中;若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并
把指定键所对应的值,替换成指定的新值。
Map 集合遍历键找值方式
键找值方式:即通过元素中的键,获取键所对应的值
分析步骤:
- 获取 Map 中所有的键,由于键是唯一的,所以返回一个 Set 集合存储所有的键。方法提
示:keyset()
- 遍历键的 Set 集合,得到每一个键。
- 根据键,获取键所对应的值。方法提示:
get(K key)
代码演示:
public class DemoMap1 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put(“黄晓明”,“杨颖”);
map.put(“邓超”,“孙俪”);
map.put(“文章”,“马伊琍”);
Set set = map.keySet();
System.out.println(set);
for (String s : set) {
map.get(s);
}
}
}
Entry 键值对对象
Map 中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在
Map 中是一一对应关系,这一对对象又称做 Map 中的一个 Entry(项)。Entry 将键值对的对
应关系封装成了对象。即键值对对象,这样我们在遍历 Map 集合时,就可以从每一个键值对
(Entry)对象中获取对应的键与对应的值。
既然 Entry 表示了一对键和值,那么也同样提供了获取对应键和对应值得方法:
public K getKey()
:获取 Entry 对象中的键。public V getValue()
:获取 Entry 对象中的值。
在 Map 集合中也提供了获取所有 Entry 对象的方法:public Set<Map.Entry<K,V>> entrySet()
: 获取到 Map 集合中所有的键值对对象的集
合(Set 集合)。
Map 集合遍历键值对方式
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
- 获取 Map 集合中,所有的键值对(Entry)对象,以 Set 集合形式返回。方法提
示:entrySet()
。 - 遍历包含键值对(Entry)对象的 Set 集合,得到每一个键值对(Entry)对象。
- 通过键值对(Entry)对象,获取 Entry 对象中的键与值。 方法提示:
getkey() getValue()
public class DemoMap2 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put(“黄晓明”,“杨颖”);
map.put(“邓超”,“孙俪”);
map.put(“文章”,“马伊琍”);
Set<Map.Entry<String, String>> set= map.entrySet();
Iterator<Map.Entry<String,String>> iter = set.iterator();
while (iter.hasNext()){
Map.Entry<String, String> a = iter.next();
a.getValue();
a.getKey();
}
// Set<Map.Entry<String,String>> set = map.entrySet();
//
// Iterator<Map.Entry<String,String>>iter = set.iterator();
// while (iter.hasNext()){
String a = iter.next();
// Map.Entry<String,String> entry = iter.next();
// entry.getKey();
// entry.getValue();
// System.out.println(entry.getValue());
// }
// getKey()
// getValue()
// for (Map.Entry<String, String> stringStringEntry : set) {
// System.out.println(stringStringEntry);
// }
}
}
tips:Map 集合不能直接使用迭代器或者 foreach 进行遍历。但是转成 Set 之后就可以使
用了。
- 当给 HashMap 中存放自定义对象时,如果自定义对象作为 key 存在,这时要保证对象唯一,
必须复写对象的 hashCode 和 equals 方法(如果忘记,请回顾 HashSet 存放自定义对象)。 - 如果要保证 map 中存放的 key 和取出的顺序一致,可以使
java.util.LinkedHashMap
集合来存放。