java基础---集合
(day13 day14)
本次内容概览:
集合体系结构
Collection集合
List集合
泛型
Set集合
Map集合
Collection工具类
一:集合体系结构
数组是个定长容器
集合是一个长度可变的容器(集合空间不够时自动扩容)
在实际开发中,当数据元素不确定的时候,集合是种最优秀的容器选择。
二:Collection集合
1.Collection的介绍和常用方法
所有集合中存储的全部都是引用数据类型,如果要涉及到基本数据类型,需要用到自动拆装箱
Collection集合是所有单列集合的顶层父接口
1--Collection集合中的常用方法
1)add(Object obj):将obj元素添加到集合中
2)remove(Object obj):将obj元素从集合中删除
3)clear():将集合中的元素清空
4)isEmpty():判断集合是否为空
5)contains(Object obj):判断集合中是否包含obj元素
6)size():返回集合中的元素个数
代码实现:
public class Demo01_Collection常用方法 {
public static void main(String[] args) {
/*
*Collection coll = new ArrayList();运用了接口的多态性
*因为Collection是一个接口,不能直接创建对象
*上句代码的意思是:Collection类型的引用,指向ArrayList类型的对象
*/
Collection coll = new ArrayList();
// 1.add(Object obj): 将参数obj添加到集合中, 末尾追加,
// 方法返回值类型boolean, 添加成功返回true,添加失败返回false
coll.add("a");
coll.add("b");
coll.add("c");
coll.add("hello");
System.out.println(coll);// [a, b, c, hello]
System.out.println(coll.isEmpty());// false
System.out.println(coll.contains("a"));// true
System.out.println(coll.contains("c1"));// false
// 2.remove(Object obj) : 将参数obj元素从集合中删除, 删除成功, 返回true,
// 如果删除失败, 返回false
System.out.println(coll.remove("c"));// true
System.out.println(coll);// [a, b, hello]
System.out.println(coll.size());// 3
// 3. clear() : 表示清空集合, 将集合中的所有元素全部删除, 集合仍然存在
coll.clear();
System.out.println(coll);// []
// 4. isEmpty() : 验证集合中是否有元素, 如果没有元素,返回true; 如果有元素, 返回false
boolean boo = coll.isEmpty();
System.out.println(boo);// true
// 5. size():表示获取到集合中元素的个数(获取集合的大小),返回值类型int类型
System.out.println(coll.size());// 0
}
}
2.Collection集合的第一种遍历方式
1)转成数组,通过遍历数组的方式,来间接的遍历集合
2)Object[] toArray():
将一个集合中的元素放到数组中(集合转数组)
将调用者集合转成Object类型的数组
Collection coll = new ArrayList();
// 1. 将集合转换成数组
Object[] objArr = coll.toArray();
3)代码实现
public class 集合的第一种遍历 {
public static void main(String[] args) {
Collection coll = new ArrayList();
//12---自动装箱成Integer
coll.add(12);
coll.add(76);
coll.add(-1);
//1.将集合转换成数组
Object co[] = coll.toArray();
//2.遍历数组相当于遍历集合
for(int i = 0;i<co.length;i++) {
//3.从Object类型数组中获取的每一个元素,Object
//类型想要转换成集合中数据类型本身,需要做多态的向下转型
Integer j = (Integer)co[i];
//System.out.println(co[i]+1);
System.out.println(j+1); //13 77 0
}
}
}
3.Collection中所有带All的方法
1)addAll(Collection c):将参数c中的所有元素,都添加到调用者集合中
2)removeAll(Collection c):从调用者集合中,删除那些也存在于参数c中的元素
3)containsAll(Collection c):判断调用者,是否能包含参数c中的所有元素
4)retainAll(Collection c):参数c中有哪些元素,就在调用者集合中保留哪些元素(交集)
5)代码实现
public class Demo03_Collection中All方法 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
Collection c1 = new ArrayList();
c1.add("Hello");
c1.add("a");
// 1. addAll(Collection c) : 将参数集合c中的所有元素添加到方法调用集合中
c.addAll(c1);
System.out.println(c);//[a, b, Hello, a]
System.out.println("--------------");
// 2. containsAll(): containsAll(Collection c) : 验证方法调用集合中的元素是否完全包含了参数c集合中的元素,如果完全包含, 返回true; 如果不是完全包含, 返回false
boolean boo1 = c.containsAll(c1);
System.out.println(boo1);// true
Collection c2 = new ArrayList();
c2.add("a1");
c2.add("b");// [a1, b]
System.out.println(c.containsAll(c2));// false
System.out.println(c);// [a, b, Hello, a]
System.out.println(c2);// [a1, b]
// 3. removeAll(Collection c) :将参数c集合中的所有元素从方法调用集合中删除掉,
// boolean类型返回值结果(删除两个集合的交集)
c.removeAll(c2);
System.out.println(c+"----");// [a, Hello, a]
// 4.retainAll(Collection c) : 将方法调用集合与参数集合c中重叠(相同)的元素保留下来,同步到方法调用集合中(获取两个集合的交集)
Collection cc = new ArrayList();
cc.add("ui");
cc.add("world");
Collection cd = new ArrayList();
cd.add("ui1");
cd.add("world1");
Collection ce = new ArrayList();
ce.add("ui");
ce.add("world____");
/*cc.retainAll(cd);
System.out.println(cc);// []
*/
cc.retainAll(ce);
System.out.println(cc);//[ui]
}
}
4.Collection集合第二种迭代器遍历方式
Iterator iterator()
1)迭代:表示从一个到下一个的过程,称为迭代
2)迭代器:专门用于将集合中的元素一个一个获取到的对象
3)迭代器对象的获取和使用:
Collection父接口中, 有一个方法 iterator() , 返回值类型Iterator(接口,表示迭代器接口), 因此iterator() 方法实际返回的是Iterator接 口的一个实现类对象
Iterator : 迭代器中的方法:
hasNext() : 判断, 集合中是否还具有下一个元素, 如果有返回true; 如果没有返回false
next() : 获取到集合中的下一个元素, 返回值类型Object
remove() : 使用迭代器对象将目前正在迭代的元素从集合中删除
4)代码实现
public class Demo04_迭代器遍历集合 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("Hello");
// 目前对于集合中有集合元素,未知,使用循环一次将元素从集合中获取到
// 1. 获取一个集合的迭代器对象
Iterator it = c.iterator();
// 2. 使用迭代器中的nextInt()方法功能判断集合中是否具有下一个元素
while(it.hasNext()) {
// 3. 集合中有下一个元素,next获取到这个元素
Object obj = it.next();
String s = (String)obj;
System.out.println(s);
}
// 4. 当集合已经遍历完毕, 但是还要通过next获取集合中的元素
// 抛出异常 : NoSuchElementException
System.out.println(it.next());
}
}
三:List集合
1.List集合的介绍和特有方法
1)概述:List集合是Collection的一个子接口
特点是:有序,有索引,可重复
2)特有方法:
A:add(int index,Object obj):在指定索引上,添加指定的元素
B:remove(int index):删除指定索引上的值
C:set(int index,Object obj):将指定索引上的值,修改为指定的值
D:get(int index):根据给定的索引,获取对应位置的值
3)代码实现:
public class Demo01_List中的特有方法 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
//1.add(int index,Object obj):在指定索引上,添加指定的元素
list.add(0,"第一");
System.out.println(list);//[第一, a, b]
//2.remove(int index):删除指定索引上的值
Object obj = list.remove(1);
System.out.println(obj); //a
System.out.println(list); //[第一, b]
//3.set(int index, Object obj):将指定索引上的值,修改为指定的值
Object obj1 = list.set(1, 78);
System.out.println(obj1);//b
System.out.println(list);//[第一, 78]
//4.get(int index):根据指定的索引,获取对应位置的值
Object obj2 = list.get(0);
System.out.println(obj2);//第一
//System.out.println(list);
}
}
2.List集合第三种遍历方式
1)是针对List集合特有的遍历方法
get(int index) : 将集合中指定index索引位置上的元素获取到
意:通过集合的size方法获取到list集合索引的范围,根据索引通过get方法获取指定索引的值
2)代码实现
public class Demo02_List第三种遍历 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
//设计循环,获取集合中的每一个索引位置
for(int i = 0;i<list.size();i++) {
//使用get(index)方法即可
System.out.println(list.get(i)); //a b
}
}
}
3.并发修改异常
1)ConcurrentModificationException 并发修改异常
2)异常发生原因 : 当使用【迭代器对象】迭代集合同时, 使用【集合对象】向集合中添加或者删除元素, 导致迭代不能保证正确性, 于是报出并发修改异常
3)异常解决方案:
A:迭代器对象迭代, 也使用迭代器对象修改
B:使用List集合特有遍历方式 : 索引
4)实际解决方式:
A:迭代器对象迭代, 也使用迭代器对象修改
在List接口中,有特殊方法:
a :listIterator() : 返回值类型ListIterator(接口), 也是一个迭代器类型,但是在原有的迭代器功能基础上,支持一边迭代集合,一边修改集合中的元素,而避免发生并发修改异常.切记一定要使用迭代器对象修改集合元素
b : ListIterator接口中的方法 :
add(Object obj) : 使用迭代器对象向集合中添加元素obj
remove() : 将迭代器正在迭代的元素从集合中删除
B:使用List集合特有遍历方式 : 索引
因为for循环只是语法结构,不是任何类型 , 因此for循环本身不会发生异常; 并且每次for循环的条件都会实时获取到集合的长度, 也会适当的保证迭代的准确性
5)代码实现:
public class Demo03_并发修改异常 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("hello");
/*需求 : 定义出一个集合,集合中有三个元素 “hello” “world” “ok” ,
* 遍历集合,当元素值匹配”hello”时,向集合中添加元素”add”*/
//并发修改异常的第一种解决方法---迭代器遍历,迭代器增加
//1.获取到List集合特有的迭代器对象ListIterator
ListIterator it = list.listIterator();
while(it.hasNext()) {
Object obj = it.next();
String s = (String)obj;
if("hello".equals(s)) {
//2.让迭代器对象给集合添加元素
it.remove();
it.add("add");
}
}
System.out.println(list);
//并发修改异常的第二种解决方法---集合遍历,集合增加
for(int i = 0;i<list.size();i++) {
String s1 = (String)list.get(i);
if("hello".equals(s1)) {
//list.add(0, "add");[add,add,a, b]造成死循环
list.add("add");
}
}
System.out.println(list);
}
}
4.List集合的实现类
(1)List接口, 需要实现类 : 根据底层实现不同,有不同的实现类
List的实现类中,使用比较多的有2个
A:ArrayList : 数组实现(ArrayList底层数组结构), 顺序存储,查询快,增删慢
B:LinkedList : 节点实现(可以利用内存中零散空间), 链式存储,查询慢,增删快
(2)LinkedList
LinkedList中的特有方法:
1)addFirst(Object obj):在头部添加元素
2)addLast(Object obj):在尾部添加元素
3)removeFirst():删除头部元素
4)removeLast():删除尾部元素
5)getFirst():获取头部元素
6)getLast():获取尾部元素
(3)代码实现
public class Demo01_LinkedList {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.addFirst("a");
list.addFirst(1);
list.addLast("end");
list.addLast("end1");
System.out.println(list);//[1, a, end, end1]
//获取头部元素
//获取尾部元素
System.out.println(list.getFirst());//1
System.out.println(list.getLast());//end1
//删除头部元素
//删除尾部元素
list.removeFirst();
list.removeLast();
System.out.println(list);//[a, end]
}
}
四:泛型
(1):泛型是引用数据类型
定义一个类型时, 对于这个类型中的一些方法参数或者返回值类型无法确定, 于是使用一个符号表示,这个符号称为泛型。
当创建出这个类型对象的时候, 需要确定泛型的具体类型。
(2):代码实现
public class Demo01_泛型的使用和好处 {
public static void main(String[] args) {
//当创建出一个ArrayList集合容器时,需要确定这个容器中存储的数据类型
//集合类型通过泛型的方式进行确定
ArrayList<Integer> as = new ArrayList();
as.add(12);
//as.add("a");
as.add(20);
as.add(88);
for(int i = 0;i<as.size();i++) {
Integer s = as.get(i);
System.out.println(s+1);
}
}
}
五:Set集合
1.Set集合介绍
(1):Set集合Collection集合的一个子接口
(2):特点:
无序,没有索引,不能重复
2.Set集合遍历(四种)
(1)方法一:toArray() : 转成数组,将集合中的元素转换到一个Object[] 类型数组中(不带泛型)(需要向下转型)
public class Demo02_Set集合遍历1 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("999");
set.add("a");
set.add("Hello");
Object[] objArr = set.toArray();
for(int index = 0; index < objArr.length; index++) {
Object obj = objArr[index];
String s = (String)obj;
System.out.println(s);
}
}
}
(2)方法二:toArray(T[] arr) : 集合转数组; 但是列表中需要提供一个与集合数据类型相同的数组类型参数, 将集合中元素同步到参数数组中, 这个数组具有指定类型, 遍历数组时候不需要向下转型; 如果arr数组大小不够集合中的元素个数, toArray方法会为你生成一个新的T[]作为方法的返回; 返回值类型T[]。(可以直接获取数据)(带泛型)
public class Demo03_Set集合遍历2 {
public static void main(String[] args) {
Set<String> set = new HashSet();
set.add("start");
set.add("hello");
set.add("end");
String a[] = new String[set.size()];
//转成数组
set.toArray(a);
for(int i = 0;i<a.length;i++) {
String s = a[i];
System.out.println(s);
}
}
}
(3)方法三:迭代器
public class Demo04_Set集合遍历3 {
public static void main(String[] args) {
Set<String> set = new HashSet();
set.add("start");
set.add("hello");
set.add("end");
//迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
(4)方法四:增强for(forEach) :
forEach语句结构:
for( 元素数据类型 变量名 : 需要遍历的集合或者数组 ){
}
说明 :
a : 元素数据类型 : 表示需要遍历的这个集合或者数组中的元素具有的数据类型
b : 变量名 : 表示集合或者数组中的每一个元素
注意 :
a : 增强for 语法底层是使用了迭代器的遍历原理
b : 使用增强for有可能会发生并发修改异常
public class Demo05_Set集合遍历4 {
public static void main(String[] args) {
Set<String> set = new HashSet();
set.add("start");
set.add("hello");
set.add("end");
//增强for
for(String s : set) {
System.out.println(s);
}
}
}
3.HashSet保证元素唯一性原理
(1)第一种解释:
元素在放入集合的过程中,首先通过HashSet的重写方法比较元素的地址值,如果地址值不重复,就放入集合中,如果地址值重复的话,就调用重写的toString方法比较元素的内容,如果内容不重复的话就放入到集合中,如果内容重复的话就不放入集合中
(2)第二种解释:(标准答案,但是比较冗长)
因为HashSet是用Hash算法来实现存取对象的,所以每个对象都有自己的hashCode. 当存储自定义类型元素时,在类中必须重写hashCode()和equals()方法。将hashCode()方法重写为属性相同的对象值返回值必须一样,属性不同的返回值尽量不一样。将equals()方法重写为属性相同时返回true,属性不同时返回false。因此,在存取对象是,HashSet会调用对象的hashCode来比较set里面是否有hashcode一样的对象,如果没有,就直接存入,如果有,就调用equals来继续比较hashcode一样的对象,如果返回值为true就不存入,如果返回值为false就存入。
4.LinkedHashSet
(1)LinkedHashSet是HashSet的一个子类, 子类中方法与HashSet中的方法一样, 同样也可以保证元素的唯一性
(2)LinkedHashSet 底层结构 : 双向链表, 能够保证添加进入到集合中的元素与从集合中取出的元素顺序一致,保证迭代顺序
(3)代码实现:
public class Demo06_LinkedHashSet {
public static void main(String[] args) {
LinkedHashSet<String> linkSet = new LinkedHashSet<String>();
linkSet.add("start");
linkSet.add("hello");
linkSet.add("end");
System.out.println(linkSet);//[start, hello, end]
}
}
六:Map集合
1.Map集合的介绍
(1):Map是双列集合顶层父接口,
Map<K,V> : 描述的键值对映射关系, 一对一 , 一个Key的值对应1个Value(Key 键,Value 值)
(2):Map集合的存储特点:
Map集合中, Key值不重复(唯一)
Map集合中, Value值可以重复
2.Map集合中的常用方法
1)增加键值对:
put(K key,V value):
2)删除方法:
remove(K key):根据给定的键,删除对应的键值对
clear:清空集合
3)获取方法
size():获取集合的大小
V get(K key):根据给定的键,获取对应的值
4)判断方法
containsKey(Object obj):判断集合中是否存在某个键
containsValue(Object obj): 判断集合中是否存在某个值
5)修改方法:
put(K key,V value):根据给定的键,修改对应的值
如果添加的key值在Map集合中不存在, 那么表示添加功能, 将键值对映射关系添加到Map集合中
如果添加的key值在Map集合中已经存在, 那么表示修改功能, 修改(覆盖)指定key对应的value值
public class Demo01_Map集合的常用方法 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap();
//1.put的添加方法
map.put(6, "hello");
map.put(8, "happy");
map.put(9, "sleep");
System.out.println(map);//{6=hello, 8=happy, 9=sleep}
//2.put的修改方法
map.put(8, "see");
System.out.println(map);//{6=hello, 8=see, 9=sleep}
//3.remove的删除方法
map.remove(6);
System.out.println(map);//{8=see, 9=sleep}
//4.size的获取方法
System.out.println(map.get(8));//see
//5.containsKey
//6.containValue
System.out.println(map.containsKey(9));//true
System.out.println(map.containsKey(10));//false
System.out.println(map.containsValue("sleep"));//true
//7.clear的清空方法
map.clear();
System.out.println(map);//{}
}
}
3.Map集合的第二种遍历KeySet
(1) keySet() : 将Map集合中的所有的Key值获取到, 放置到一个Set集合中
(2)遍历含有Key值的Set集合, 获取到每一个Key值, 然后通过get(Key), 通过每一个Key值获取到齐对应的Value值
(3)代码实现
public class Demo02_Map的第一种遍历方式 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap();
//1.put的添加方法
map.put(6, "hello");
map.put(8, "happy");
map.put(9, "sleep");
// 2. 获取到map中的所有的key的值
Set<Integer> setK = map.keySet();
// 3. 遍历setK获取到每一个Key值
for(Integer key : setK) {
// 4.通过get方法获取对应的value值
String value = map.get(key);
System.out.println(key+"------"+value);
}
}
}
4.Map集合的第二种遍历entrySet
(1):entrySet() : 将Map集合中的所有的键值对关系获取到(成对的数据), 放置到一个Set集合中, Map集合中的每一对数据就是 Map.Entry<K,V>类型
(2):遍历装有Map.Entry<K,V>键值对类型的Set集合, 获取到每一对关系
(3):在Map.Entry<K,V>类型中, 有方法能单独获取到一对元素中的key和value
getKey() : 获取到键值对中key值, 返回值类型K
getValue() : 获取到键值对中key值, 返回值类型V
(4):代码实现
public class Demo03_Map的第二种遍历方式 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap();
//1.put的添加方法
map.put(6, "hello");
map.put(8, "happy");
map.put(9, "sleep");
//1.获取到Map集合中所有键值对数据
Set<Map.Entry<Integer,String>> set = map.entrySet();
//2.遍历set集合,将每一对映射关系获取到
for(Map.Entry<Integer, String>entry:set) {
//3.获取到一对关系中的key和value
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"????"+value);
}
}
}
5.HashMap
(1):HashMap : Map双列集合实现类, 底层哈希表结构
(2):HashMap 集合对于元素的存取无序, Map集合没有索引
(3):HashMap集合中的key值唯一, 保证key值不重复实现原理与HashSet一致
如果HashMap存储JDK提供类型作为key, 直接保证key不重复
如果HashMap存储自定义的类型作为key, 要求自定义类型中重写hashCode和equals两个方法(因为自定义类型无法保证 键的唯一性)
(4): HashSet去重复原理就是使用了hashMap中的key值无重复原理
public class Demo04_HashMap存储自定义类型 {
public static void main(String[] args) {
HashMap<Person,String> map = new HashMap<>();
map.put(new Person("zs",19), "新加坡");
map.put(new Person("lis",20), "香港");
System.out.println(map);
//{Person [name=zs, age=19]=新加坡, Person [name=lis, age=20]=香港}
}
}
6.LinkedHashMap
(1):LinkedHashMap 是HashMap的子类, 方法功能与HashMap一致, 但是LinkedHashMap维护了双向链表,
效果 : 保证元素 迭代顺序 (元素的存取顺序一致)
(2):代码实现
public class Demo05_LinkedHashMap {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(11, "a");
map.put(13, "a");
map.put(12, "c");
System.out.println(map);// {11=a, 12=c, 13=a}
LinkedHashMap<Integer, String> map1 = new LinkedHashMap<Integer, String>();
map1.put(11, "a");
map1.put(13, "a");
map1.put(12, "c");
System.out.println(map1);// {11=a, 13=a, 12=c}
}
}
七:Collection工具类
- Collections 来自于java.util包, 对于单列集合(List和Set)封装了很多常用的方法功能
- Collections工具类,没有提供构造创建对象, 所有的成员变量和方法功能都是静态,类名.直接调用
- Collections常用方法:
(1):sort(List list) : 将参数list集合中的元素进行升序排列(从小到大)
(2):binarySearch(List<T> list, T key): 需要一个升序排列的集合, 寻找key值在list集合中出现的索引位置, 如果找不到元素,索 引就为负数
(3):max(Collection c): 将集合中的最大值获取到
(4):min(Collection c): 将集合中的最大值获取到
(5):replaceAll(List<T> list, T old, T newE) : 将集合list中的所有old元素替换为newE
(6):reverse(List list) : 将list集合中元素进行逆序排序
(7):shuffle(List list) : 将list集合中的元素进行随意的打乱
(8):swap(List list, int index1, int index2) : 将list集合中index1和index2索引位置的元素进行替换
4:代码实现
public class Demo06_Collections {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(0);
list.add(1);
list.add(9);
list.add(4);
System.out.println(list);//[0, 1, 9, 4]
// 1.sort(List list) : 将参数list集合中的元素进行升序排列(从小到大)
Collections.sort(list);
System.out.println(list);//[0, 1, 4, 9]
// 2.binarySearch(List<T> list, T key): 需要一个升序排列的集合,
// 寻找key值在list集合中出现的索引位置, 如果找不到元素,索引就为负数
int index = Collections.binarySearch(list, 4);
System.out.println(index);// 2
// 3.max(Collection c): 将集合中的最大值获取到
int max = Collections.max(list);
// 4.min(Collection c): 将集合中的最小值获取到
int min = Collections.min(list);
System.out.println(max + "---" + min);// 9-----0
// 5.replaceAll(List<T> list, T old, T newE) : 将集合list中的所有old元素替换为newE
Collections.replaceAll(list, 1, 110);
System.out.println(list);//[0, 110, 4, 9]
// 6.reverse(List list) : 将list集合中元素进行逆序排序
Collections.reverse(list);
System.out.println(list);//[9, 4, 110, 0]
/* // 7.shuffle(List list) : 将list集合中的元素进行随意的打乱
Collections.shuffle(list);
System.out.println(list);*/
// 8. swap(List list, int index1, int index2) : 将list集合中的index1和index2索引位置的元素进行替换
Collections.swap(list, 1, 2);
System.out.println(list);//[9, 110, 4, 0]
}
}