集合的概述
单列集合的体系:
Collection 单列集合的顶层接口
List Collection的子接口,增加了一些特有的方法,有序 可以重复 有索引
ArrayList 底层采用的数组结构实现,顺序存储,查询修改快
LinkedList 底层采用的是双向链表实现,链式存储,增加删除快
Set Collection的子接口,无序 不可以重复 没有索引
HashSet 哈希表存储(数组+单向链表)
LinkedHashSet 可以维护集合元素的顺序
双列集合的体系
Map 双列集合的顶层接口
HashMap 采用的是哈希表进行数据存储
LinkedHashMap 可以维护Map集合中键的顺序
Collection概述
1、单列集合的顶层接口,定义了所有单列集合共有的方法(功能)
2、Collection是一个接口,不能直接创建对象的,找一个实现类去
常用的方法
1、add(Object obj),将obj这个元素添加到集合中
2、remove(Object o)将o这个元素从集合中移除
3、isEmpty();判断集合是否为空
4、size()方法 ,获取集合中元素的个数
5、clear();将集合中的元素清空
6、contains(Object obj); 判断集合中是否包含obj元素
拓展:
数组中使用length,length属性
字符串中 length() 方法
集合中,size()方法 ,获取集合中元素的个数
Collection中带all的方法:toArray()
1、addAll(Collection c)将参数中的c集合中所有的元素,添加到调用者集合中
2、containsAll(Collection c) 如果调用者集合中包含参数集合中所有的元素,返回true否则返回false
3、retainAll(Collection c) 保留调用者集合和参数集合中共有的元素
4、removeAll(Collection c) 移除调用者集合中和参数集合中相同的元素
集合遍历的第一种方式
将集合转成数组,通过遍历数组的方式,间接的遍历集合
Object[] toArray() 将调用者集合装成object类型的数组
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(520);
coll.add(true);
coll.add("大熊猫");
coll.add('a');
coll.add(12.23);
coll.add(new Person("魏无羡", 34));
System.out.println(coll);
// 将集合转成数组
Object[] objs = coll.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i]);
}
}
集合遍历的第二种方法:迭代器
itertor()方法返回的是一个Itertor接口的实现类对象
hasNext();判断集合中是否还有下一个元素
next();获取集合中的下一个元素
remove():删除迭代器正在迭代的对象
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Person("肖战", 23));
coll.add(new Person("蔡徐坤", 20));
coll.add(new Person("卢本伟", 24));
coll.add(new Person("PDD", 30));
coll.add(new Person("大司马", 32));
// 通过调用itertor方法,获取该集合的迭代器对象
Iterator it = coll.iterator();
// hasNext()
while (it.hasNext()) {
// 不要使用hasNext()判断一次,就调用多次next方法
Object next = it.next();
Person p = (Person) next;
System.out.println(p.getName() + ":::" + p.getAge());
}
拓展:集合遍历的第三种方法:foreach
增强for(foreach)
格式:
for(元素的数据类型 元素名称 :要遍历集合){
}
注意事项:
使用增强for,拿不到元素的索引,无法修改集合或者数组中的元素值
底层是迭代器,所以在遍历的时候,使用集合对象增加或者删除元素的时候,会发生并发修改异常
public static void main(String[] args) {
// 创建一个集合
Collection coll = new ArrayList();
// 在集合中存储元素
coll.add("a");
coll.add("b");
coll.add("d");
for (Object object : coll) {
System.out.println(object);
}
}
List集合(Collection子接口)
特点:
元素可以重复
可以索引
有序
方法:
从Collection根接口中继承下来的方法(上述常用方法和带all的方法)
在继承的基础上有一些关于下标(索引)的特有的方法:
1、get(int index)获取arraylist中指定索引的元素
2、set(int index,Object obj),修改arraylist中索引为index的元素为obj
3、remove(int index)移除arraylist中指定索引的元素,移除int类型的时候要注意
4、add(int index,Object obj)在索引为index的位置添加元素
List集合的实现类—ArrayList
ArrayList集合是List接口中比较重要的一个实现类
底层实现是:数组
特点: 查询快 增删慢
public static void main(String[] args) {
ArrayList arrlist = new ArrayList();
arrlist.add(234);
arrlist.add(123);
arrlist.add(0,"任正非");
arrlist.add(0,"马化腾");
System.out.println(arrlist);
// get(int index)获取arraylist中指定索引的元素
System.out.println(arrlist.get(1));
// set(int index,Object obj)
arrlist.set(3, "马云");
System.out.println(arrlist);
// remove(int index)
// 注意:集合中的元素存储的是 Integer 类型的元素时,remove方法中的参数 默认是下标,当明确参数类型是integer的时候 调用的 remove(Object obj).
arrlist.remove(new Integer(234));
arrlist.remove(0);
System.out.println(arrlist);
// 迭代器遍历
Iterator it = arrlist.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
List集合的实现类—LinkedList
LinkedList:也是List集合的实现类
底层实现方式:采用双向链表实现
每一个元素都存储在一个节点中,节点除了元素本身以外,还需要存储下一个元素的内存地址
特点:查询修改慢 增加和删除快
LinkedList中特有的方法:
addFirst()在头部添加元素
addLast()在尾部添加元素
removeFirst()移除头部元素
removeLast()移除尾部元素
getFirst()获取第一个元素
getLast()获取最后一个元素
List集合特有遍历方式
可以通过集合中size()方法和索引进行结合,使用get(index)方法完成对List集合的遍历
package com.DaYu;
import java.util.ArrayList;
import java.util.List;
public class Demo03 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("肖战");
list.add("蔡徐坤");
list.add("卢本伟");
list.add("PDD");
list.add("大司马");
System.out.println(list);
for(int i = 0;i<list.size();i++) {
System.out.println(list.get(i));
}
}
}
并发修改异常
1、ConcurrentModificationException
并发修改异常
2、原因
迭代器对象在遍历集合的时候,使用集合对象进行增加和删除的操作(修改不会造成异常)
3、避免方式:两种避免的方式都是针对List集合
方式1:迭代器遍历,迭代器增加和删除
方式2:集合遍历,集合增加和删除
4、列表迭代器:ListIterator,是Iterator 下面的一个子接口,拥有Iterator中的方法,还有特有的方法,可以实 现迭代器遍历迭代器添加。获取ListIterator对象的方式是通过List集合调用listIterator()方法
集合遍历,集合添加:list特有的遍历方式,结合size和get方法,集合添加add
package com.DaYu;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Demo03 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("肖战");
list.add("蔡徐坤");
list.add("卢本伟");
list.add("PDD");
list.add("大司马");
System.out.println(list);
for(int i = 0;i<list.size();i++) {
if(list.get(i).equals("PDD"))
list.add("DNF");
}
System.out.println(list);
Iterator it = list.iterator();
while(it.hasNext()) {
Object next = it.next();
if(next.equals("PDD")) {
it.remove();
}
}
System.out.println(list);
ListIterator itt = list.listIterator();
while(itt.hasNext()) {
Object next = itt.next();
if(next.equals("DNF")) {
itt.add("djx");
}
}
System.out.println(list);
}
}
拓展知识(面试题)
Vector ArrayList LinkedList 三者的区别:
集合体系是在jdk1.2 版本中建立起来,在这之前用的是Vector,从1.2开始Vector属于Collection接口的实现类。
Vector:和ArrayList 底层都是数组实现,只不过线程安全的,效率低下。
ArrayList:底层数组实现 但是线程是不安全,效率高。
LinkedList :和ArrayList都是List接口的实现类,但是底层实现是不同的,LinkedList采用的双向链表的结构进行存储元素,也是线程不安全的。
思考:为什么ArrayList查询修改元素的速度快,增加删除元素慢?
每一个元素是挨着存储的存储空间是连续的,通过首地址和下标就能够很快地定位到每个元素,所以查询和修改快,但是想要添加或删除一个元素时,如果在中间或者头部插入,插入点后面的元素都要移动,所以增加和删除就会很慢。
Set集合(Collection子接口)
1、Set是Collection的一个子接口
2、特点:
无序:没有任何的前后之分,所有的元素没有位置的概念,所有的元素都在集合中
没有索引:集合中没有任何的位置,元素没有位置的属性
不能重复:没有位置的区分,同样的元素没有任何区别,所以不能重复
3、实现类:
HashSet:使用哈希表的存储方法区存储元素
Set集合的遍历
1、没有自己特有的遍历方式,只能使用Collection接口中定义的方法,只能使用Collection中的遍历方式
2、第一种:转成数组,toArray();不带泛型,遍历数组
第二种:转成数组,toArray(T[] arr)带着泛型的数组,得到的是T类型的数组
(1)自己创建的数组大小,小于集合中元素的个数
在调用toArray(T[] arr) 的时候,会返回一个新的数组,并将集合中的元素填充到新数组中
(2)自己创建的数组大小,等于集合中元素的个数
在调用toArray(T[] arr) 的时候,就不会创建新的数组,直接就将集合中的元素填充到数组中,并返回
(3)自己创建的数组大小,大于集合中元素的个数
在调用toArray(T[] arr) 的时候,直接就将集合中的元素填充到数组中,但是对于数组中剩余的位置, 用数组类型的默认值进行填充
3、第三种:迭代器
4、增强for(foreach)
练习
随机生成10个20-40之间的随机数,存储在合适的集合中,并进行遍历,随机数不能重复的
package com.DaYu;
import java.util.HashSet;
import java.util.Set;
public class Demo01 {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
while(set.size() < 10) {
int num = (int)(Math.random() * 21 + 20);
set.add(num);
}
for (Integer i : set) {
System.out.println(i);
}
}
}
HashSet存储自定义类型元素去重原理
1、HashSet是用Hash算法来实现存取对象的,每个对象都有自己的hashCode。
2、当存储自定义类型元素时,在类中必须重写hashCode()和equals()方法。
将hashCode()方法重写为属性相同的对象值返回值必须一样,属性不同的返回值尽量不一样。
将equals()方法重写为属性相同时返回true,属性不同时返回false。
3、在存取对象时,HashSet会调用对象的hashCode来比较集合里面是否有哈希值一样的对象,如果没有,就直接存入,如果有,就调用equals来继续比较hashcode一样的对象,如果返回值为true就不存入,如果返回值为false就存入。
LinkedHashSet
1、是HashSet的一个子类,和HashSet保证元素唯一性的原理相同
2、将每个元素在存储的时候,记录了前后元素的地址
3、效果
可以根据存储元素的顺序,将元素取出
4、使用:
既要保证元素的唯一,又要保证原来的顺序,就可以使用LinkedHashSet
package com.DaYu;
import java.util.LinkedHashSet;
import java.util.Scanner;
public class Demo02 {
// 键盘录入一个字符串,输出其中的字符,并且相同的字符只输出一次,要保证原来的顺序
public static void main(String[] args) {
// 创建一个LinkedHashSet用来存储字符
LinkedHashSet<Character> lsh = new LinkedHashSet<Character>();
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.nextLine();
// 将字符串转成字符数组
char[] chs = str.toCharArray();
for (char c : chs) {
lsh.add(c);
}
System.out.println(lsh);
// 创建一个StringBuilder对象用来拼接字符
StringBuilder sb = new StringBuilder();
// 遍历linkedHashSet集合
for (char c : lsh) {
sb.append(c);
}
System.out.println(sb);
}
}
List和Set分别有哪些实现类?各个实现类有什么特点
List的实现类包括:ArrayList,LinkedList。
ArrayList和LinkedList的共有特性包括存取是有序的,元素是可重复的,元素是有序列的。
ArrayList的查询速度快,但是增删速度比较慢,
LinkedList的查询速度慢,但是增删速度比较快。
Set的实现类包括:HashSet和LinkedHashSet。
Set的实现类的共有特点包括存取无序,元素不可重复,元素无序列。
HashSet和LinkedHashSet都是用hash算法来存取集合的对象的,LinkedHashSet和HashSet不一样的区别是LinkedHashSet实际上是存取是有顺序的,是无序set中的有序set。
泛型
1、泛型的概述和使用
泛型:广泛的类型,在定义一个类的时候,类型中有些方法参数,返回值类型不确定,就使用一个符号,来表示那么尚未确定的类型,这个符号称为泛型
2、注意事项
1、前后一致:在创建对象的时候,赋值符号前面和后面的泛型要保持一致
2、泛型推断:jdk1.7之后如果前面所属的泛型已经确定了,后面的泛型可以不写(尖括号中可以什么都不写),只写一个<>就可以了
3、不能定义泛型数组,因为如果可以定义泛型数组就会发生泛型擦除,失去了泛型的作用,也就失去了泛型存在的意义
// 有泛型的情况
List<String> list = new ArrayList<>();
// list.add(123);//不属于String类型,不能添加进去
list.add("asdfff");
list.add("assdf");
list.add("asdsf");
System.out.println(list);
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String next = it.next();
System.out.println(next);
}
// 没有泛型的情况
List list = new ArrayList();
list.add(123);
list.add(456);
list.add("java");
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
Map概述
1、概述
Map双列集合的顶层接口
2、特点
key(键)是唯一的,value(值)不唯一的
每个键都对应唯一的值
3、Map和Collection的区别
Map就是双列集合,是以键值对的形式存放数据,Collection是单列集合,存放就是单个元素
Map中的键是唯一的,Collection中set集合的元素是唯一的
Map的所有操作针对键有效,Collection中针对的是元素
Map中常用的方法
1、增加键值对的方法:put(K key,V value)
2、 remove(Object key) 根据给定的键,删除对应的键值对
clear();清空集合
3、获取方法
get(Object Key) 根据指定的键,获取该键对应的值
size() 获取map集合中键值对的个数
4、判断方法
containsKey(Object Key) 判断集合中是否存在某个键
containsValue(Object value)判断集合中是否存在某个值
5、修改方法
根据给定的键,修改对应的值:put(K key, V value)
如果集合中存在key这个键,那么使用put方法就是修改对应的值,如果集合中不存在key这个键,使用put方法就是在 集合中添加了一个键值对
HashMap
1、就是Map接口的实现类
2、HashMap存储jdk中提供的元素时,可以直接保证元素的唯一性
3、HashMap存储自定义类型的时候,键无法保证元素的唯一性的,要想保证键的元素的唯一性,必须重写HashCode方法和 equals方法
HashMap键的唯一性和HashSet的元素唯一性,保证的方式一样的
4、HashMap和HashSet的关系
HashSet是由HashMap实现的,HashSet就是HashMap中键的那一列
将HashMap中的值那一列隐藏掉,就变成了HashSet
LinkedHashMap
1、是HashMap的一个子类
2、跟HashMap不同之处在于,具有可预知的迭代顺序,存储的键值对和取出的键值对的顺序能够保持一致
Map集合中遍历的第二种思路
1、获取Map集合中所有的键,放到一个Set集合中,遍历Set集合,得到Map集合中的每一个键
根据键再来获取对应的值
2、获取Map集合中的所有键
Set keySet();
3、遍历set集合两种方式
迭代器
增强for循环,foreach
4、拿到Set集合中的每一个键的时候,根据map中的get方法获取键所对应的值
Map集合中遍历的第二种思路
1、获取的map集合中所有的键值对对象(Entry),到set集合中,拿到的是每个键值对对象(Entry),从这个对象中分别获取键和值(根据键值对对象(Entry)提供的方法)
2、根据map集合获取所有的键值对对象,到一个set集合中
Set<Entry<K,V>> entrySet();
3、遍历set集合,两种遍历方式
迭代器
增强for循环
4、遍历Set集合的时候,每一次拿到的都是一个键值对对象
Entry是Map集合中的一个内部接口访问的方式是Map.Entry
Entry常用的方法
getKey():获取键值对对象的键
getValue();获取键值对对象的value值
package com.DaYu;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
//(1)定义一个学生类Student,包含属性:姓名(String name)、年龄(int age)
//(2)定义Map集合,用Student对象作为key,用字符串(此表示表示学生的住址)作为value
//(3)利用四种方式遍历Map集合中的内容,格式:key::value
public class HomeWork01 {
public static void main(String[] args) {
Student s1 = new Student("大毛", 11);
Student s2 = new Student("二毛", 12);
Student s3 = new Student("三毛", 13);
Student s4 = new Student("四毛", 14);
Map<Student, String> map = new HashMap<>();
map.put(s1, "江苏");
map.put(s2, "浙江");
map.put(s3, "四川");
map.put(s4, "河南");
System.out.println(map);
Set<Student> set = map.keySet();
for (Student student : set) {
System.out.println(student + "::" + map.get(student));
}
System.out.println("---------------------------------");
Iterator<Student> it = set.iterator();
while(it.hasNext()) {
Student next = it.next();
System.out.println(next+ "::" + map.get(next));
}
System.out.println("---------------------------------");
Set<Entry<Student, String>> set2 = map.entrySet();
for (Entry<Student, String> entry : set2) {
System.out.println(entry.getKey()+ "::" + entry.getValue());
}
System.out.println("---------------------------------");
Iterator<Entry<Student, String>> it2 = set2.iterator();
while(it2.hasNext()) {
Entry<Student, String> next = it2.next();
System.out.println(next.getKey()+ "::" + next.getValue());
}
}
}
Collections工具类
1、常用的方法
binarySearch(List list, T key);在一个有序的List集合中,通过二分查找,找到key所对应的索引
frequency(Collection c ,Object obj)返回的是obj这个元素在c集合中出现的次数
replaceAll(List list, T oldVal, T newVal) 将list集合中的oldval都替换为newVal
shuffle(List<?> list) 将list集合中的元素进行随机置换
swap(List<?> list, int i, int j) 在指定列表的指定位置处交换元素。
synchronizedxxx方法系列:将一个线程不安全的集合传入方法,返回一个线程安全的集合
unmodifiablexxx方法系列:将一个可修改的集合传入方法,返回一个不可修改只读的集合
Collection和Collections的区别
Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。