集合基础总结
1、集合和数组的区别
(1) 相同点:集合和数组,都是用来存储数据的
(2) 不同点:
① 数组:
-
可以存储基本数据类型
-
可以存储引用数据类型
-
是一个定长的容器
② 集合:
-
只能存储引用数据类型
-
如果存储基本数据类型,只能存储对应的包装类,因为有自动装箱,所以看起来可以直接存储,但是实际存储的类型是包装类
-
集合可以认为是一个可以伸缩的容器
2、集合的分类
(1) 单列集合:每一个元素都是一个单独的个体,存储单个元素的
(2) 双列集合:每一个元素都是一对数据(存储的都是键值对)
大致分类图解
单列集合的顶层接口
1、Collection是单列集合的顶层接口,定义了所有单列集合的共有的内容,jdk不提供此接口的任何直接实现,他提供了更具体的子接口(Set、List);
2、创建Collection集合对象
(1) Collection接口,不能直接创建对象,需要找一个例如:ArrayList创建对象
(2) 多态的方式:接口类型的引用指向实现类对象,Collection c = new ArrayList();
3、Collection集合常用的方法:
(1) boolean add(Object o):添加元素
(2) boolean remove(Object o):删除元素
(3) void clear():清空集合中的元素
(4) boolean contains(Object o):判断集合中是否存在指定元素
(5) boolean isEmpty():判断集合是否为空 空不是null 如果为空,返回true
(6) int size():返回集合中元素的个数,集合的长度
(7) Object[] toArray():将集合存储的内容,转存到一个数组内
public class Demo01 {
public static void main(String[] args) {
//创建一个Collection集合,因为Collection是一个接口
Collection c = new ArrayList();
c.add("Aisino");
c.add("航天信息");
//集合是不能添加基本数据类型的
//如果要添加,jdk1.5之后,有自动装箱,会自动转为对应的包装类型对象进行存储
//所以我们看起来添加了int,其实是添加了一个Integer类型
c.add(1);
//c是字符类型 存储的也是对应的包装类对象Character
System.out.println(c.add('c'));
System.out.println(c.add(true));
System.out.println(c);
//判断集合中是否包含指定元素 contains(Object o)
System.out.println(c.contains(1));
System.out.println(c.contains('c'));
//删除集合中的某个元素 remove(Object)
System.out.println(c.remove('c'));
System.out.println(c);
//清空集合 clear()
// c.clear();
// System.out.println(c);
//判断集合是否为空 isEmpty();
//空代表的是集合存在,但是里面没有内容不是null
//c = null; 相当于此时集合c不存在了
System.out.println(c.isEmpty());
System.out.println(c.size());
//toArray 将集合转为数组
Object[] o = c.toArray();
String s = (String)o[0];
for (int i = 0; i < o.length; i++) {
System.out.println(o[i]);
}
}
}
单列集合的遍历
迭代器遍历
1、迭代器:专门针对单列集合进行遍历的对象,称为迭代器,是单利集合专用的遍历方式
2、获取集合迭代器的方式:
(1) Iterator iterator(): 返回次集合中的元素的迭代器,通过集合的iterator()方法获取到
(2) 返回值类型 Iterator接口:表示对Collection单列集合进行遍历的迭代器
(3) 此时的返回的内容实际上就是该接口的实现类对象
3、Iterator的常用的方法:
(1) boolean hasNext():判断当前位置是否有元素可以被取出
(2) Object next():获取当前位置的元素,将迭代器对象指向的位置向下移动一格
(3) void remove():删除迭代器对象当前指向的元素
public class Demo02 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
//第一步,先获取迭代器对象
Iterator it = c.iterator();
//第二步,判断当前位置是否有元素
//如果有元素,就取出,没有元素就终止
while(it.hasNext()){
//第三步,取出当前位置的元素,并移动it指向的位置
Object next = it.next();
System.out.println(next);
//迭代器对象的remove方法:删除当前指向位置的元素
if (next.equals("a")){
it.remove();
}
}
System.out.println(c);
}
}
增强for循环遍历
1、概述:
(1) 增强for循环底层原理是一个Iterator迭代器
(2) 只有实现Iterable接口的类才可以使用迭代器和增强for循环,简化数组和集合的遍历
2、格式:
(1) for(集合元素的类型 变量名 : 要遍历的集合){
}
(2) for(数组的元素类型 变量名 : 要遍历的数组){
}
此时变量名就表示未来一个一个获取出的元素,每次变量名中存储的都是不同的内容
代码
public class Demo03 {
public static void main(String[] args) {
int[] arr = {1,2,3,4};
for (int i : arr) {
System.out.println(i);
}
Collection c = new ArrayList();
c.add("a");
c.add("B");
c.add("C");
c.add("d");
for (Object o : c) {
System.out.println(o);
}
}
}
有序的单列集合List
1、List集合是Collection接口的子接口,其下有两个常用的实现类ArrayList和LinkedList
2、List集合的特点:
(1) 有序:元素存入的顺序和取出的顺序一致
(2) 有索引:每个元素都有自己的索引,从0开始到集合长度-1
(3) 元素可以重复:即使是值相同的几个元素,索引也各不相同,可以区分这几个元素的值
3、List集合特有的方法:
(1) void add(int index,Object o):在集合中指定位置插入指定的元素
(2) Object remove(int index):删除指定索引的元素,返回值就是被删除的元素
(3) Object set(int index,Object o):修改指定索引出的元素,返回被修改的元素
(4) Object get(int index):返回指定索引位置的元素
4、Collection下有个size()方法,可以获取元素个数,有元素个数,就相当于有了索引范围,也就意味着可以通过集合的size()方法,获取list的索引范围,根据索引范围通过get方法,可以获取每一个元素的值
代码
public class Demo04 {
public static void main(String[] args) {
//创建List集合
//List是一个接口 也是不可以创建对象
List l = new ArrayList();
//List是Collection的子接口,所以可以使用Collection中的add方法
//也可以使用List下特有的add方法
l.add("abc");
//插入 在指定位置上插入元素,将aaa放在0的索引位置上
l.add(0,"aaa");
//将bbb放入0的位置 效果就是原来的元素,向后移动
l.add(0,"bbb");
System.out.println(l);
//使用索引值添加元素的时候,要确保添加位置前面都已经有元素了
//l.add(4,"ccc");
l.add(3,"ccc");
System.out.println(l);
//remove(Object o)删除集合中指定的元素
//remove(int index)删除指定位置的元素,并返回该元素的内容
Object remove = l.remove(3);
System.out.println(remove);//返回值就是删除的元素
System.out.println(l);
//set(int index,Object o) 修改指定位置的元素,并返回原来的元素
Object h = l.set(0, "哈哈哈哈");
System.out.println(h);
System.out.println(l);
//获取指定位置的元素 get(int index);
System.out.println(l.get(0));
}
}
List下特有的遍历方式
public class Demo05 {
public static void main(String[] args) {
List l = new ArrayList();
l.add(0,"a");
l.add(0,"b");
l.add(0,"c");
l.add(0,"d");
l.add(0,"e");
System.out.println(l.size());
System.out.println(l);
for (int i = 0; i < l.size(); i++) {
//有索引,就可以根据get方法获取对应的元素
System.out.println(l.get(i));
}
System.out.println("~~~~~~~~~~~");
for (Object o : l) {
System.out.println(o);
}
System.out.println("~~~~~~~~");
//迭代器
Iterator it = l.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
for循环遍历删除
public class Demo01 {
public static void main(String[] args) {
List l = new ArrayList();
l.add(new Person("张三",11));
l.add(new Person("李四",18));
l.add(new Person("王五",20));
l.add(new Person("赵六",22));
for (int i = 0; i < l.size(); i++) {
Object o = l.get(i);
Person p = (Person)o;
if(p.getName().equals("王五")){
//删除当前位置的元素
//如果想用对象的方式进行删除,需要重写Person类中的hashCode方法
//hashCode方法重写之后,对于相同属性值的对象,计算出的值是相同的
//l.remove(new Person("赵六",22));
//在王五后面添加一个元素田七
l.add(i+1,new Person("田七",33));
}
}
System.out.println(l);
}
}
class Person{
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", 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;
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
并发修改异常
1、ConcurrentModificationException:并发修改异常
2、异常产生的原因:在迭代器遍历的过程中使用集合对象对元素进行添加和删除
3、解决办法:
(1) 通过for循环遍历集合,在遍历的过程中可以添加和删除元素
(2) 使用迭代器的remove方法
(3) 使用listIterator()
① add(Object o):添加元素
② remove() : 移除元素
迭代器遍历删除
public class Demo02 {
public static void main(String[] args) {
List l = new ArrayList();
l.add(new Person("张三",11));
l.add(new Person("李四",18));
l.add(new Person("王五",20));
l.add(new Person("赵六",22));
Iterator it = l.iterator();
while(it.hasNext()){
Object o = it.next();
Person p = (Person)o;
if(p.getName().equals("赵六")){
//ConcurrentModificationException 并发修改异常
//产生的原因就是在迭代器的遍历的过程中,使用集合对象进行了增删集合中的元素
//修改了集合的长度
//此时在迭代器中进行遍历集合的每一个元素
//迭代器依存于集合
//而我们直接修改了集合的内容,导致迭代器遍历判断就发生了问题
//l.remove(new Person("王五",20));
//迭代器中想要删除目标元素,一定使用迭代器对象提供的remove()方法
//删除当前指向位置的元素
it.remove();//it指向哪个元素,就是删除哪个元素
}
System.out.println(p);
}
System.out.println(l);
}
}
List下特有的迭代器
1、迭代器ListIterator,获取方法也是listIterator()
2、操作原理和iterator一样
3、特有的方法:
(1) add(Object o)在当前指向位置后添加元素
(2) remove():删除当前指向位置的元素
public class Demo03 {
public static void main(String[] args) {
List l = new ArrayList();
l.add(new Person("张三",11));
l.add(new Person("李四",18));
l.add(new Person("王五",20));
l.add(new Person("赵六",22));
ListIterator it = l.listIterator();
while(it.hasNext()){
Object next = it.next();
Person p = (Person) next;
if(p.getName().equals("李四")){
//添加一个元素 ListIterator特有的添加方法
//在当前it指向位置后面添加元素
it.add(new Person("金",22));
//删除一个元素和iterator是相同的
}
}
System.out.println(l);
}
}
数据结构之数组和链表
1、数组:
(1) Array:是有序的元素序列,数组是在内存中开辟一块连续的空间,并在此空间中存放元素,就像是一排房间,现在假设有100个房间,从001到100每个房间都有固定编号,每个房间间距4米远,通过编号就可以快速找到对应的房子
(2) 特点查找元素快
① 可以快速根据编号找到对应的房间
(3) 增删元素慢
① 指定索引位置添加元素:需要创建一个新数组,将指定元素存储在索引位置,再把原数组元素根据索引,赋值到新数组对应的索引的位置
链表
1、Linked List,由一系列结点node(链表中的每一个元素被称为结点)组成,结点可以在存储是动态生成,每个结点包含两部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域,我们常说的链表结构有单向链表与双向链表,那么在这说的是单向链表
2、采用链表结构存储的元素,对元素存取有以下的特点:
(1) 多个结点之间使用地址链接。
ArrayList
1、数组实现,增删慢,查改快
(1) 当创建ArrayList 集合的时候,会维护一个长度为10的Object类型的数组
(2) 当插入数据10个长度不够的时候,此时会以1.5倍的形式进行扩容
(3) 存储特点:增删慢,查改快
2、实际开发中,ArrayList用的最多,很多功能都需要用到统计查询
3、ArrayList查改快,数组的第一个元素的位置,被称为首地址,根据首地址+索引偏移量就可以直接算出目标元素的位置,从而快速定位
4、增删慢:想要在中间添加元素,相对比较慢的,添加元素位置以后的所有的元素,都要向后移动一位,集合规模越大,所需要的时间越多
代码
public class Demo04 {
public static void main(String[] args) {
//没有特有的方法,使用list下的方法,使用ArrayList集合的特点
ArrayList list = new ArrayList();
list.add("123");
}
}
LinkedList
1、集合存储的元素是双向链表,方便元素添加和删除
2、该类型有大量的对于头部、尾部元素操作的方法
3、特点:查询慢、增删快,链表结构不需要连续的空间,可以利用内存中零散的空间进行存储
4、常用的方法:
(1) addFirst(Object o):将指定元素插入此列表的头部
(2) addLast(Object o):插入尾部
(3) getFirst():获取头部元素
(4) getLast():获取尾部元素
(5) removeFirst():删除头部元素
(6) removeLast():删除尾部元素
代码
public class Demo05 {
public static void main(String[] args) {
LinkedList l = new LinkedList();
//头部添加元素
l.addFirst("张三");//第一次头部张三
l.addFirst("李四");
l.addFirst("王五");
//尾部添加元素
l.addLast("赵六");
l.addLast("田七");
System.out.println(l);
Object first = l.getFirst();
//获取头部元素
System.out.println(first);
//获取尾部元素
Object last = l.getLast();
System.out.println(last);
//删除头部元素 返回被删除的元素
Object o = l.removeFirst();
System.out.println(o);
//删除尾部元素
Object o1 = l.removeLast();
System.out.println(o1);
}
}
无序单列集合Set
1、Set是一个接口,接口中的方法都是继承自Collection,没有什么特有的方法,所以只需要研究特点即可,无序,不可以重复
2、无序:没有任何前后的分别,存入的顺序和取出的顺序不一定一致(Set有自己的对于元素确定其存放位置的规则,只不过不是按照先来后到的顺序)
3、没有索引:集合中没有任何位置,元素也就没有索引的概念
4、不能重复:没有任何位置的区别,相同元素没有任何区别,所以不能重复
5、Set常用的实现类:HashSet(常用) TreeSet(可以自定义元素存储先后顺序规则的一个实现类)
6、Set集合的遍历方式:迭代器和增强for
代码
public class Demo01 {
public static void main(String[] args) {
Set<String> s = new HashSet<>();
s.add("Aisino1");
s.add("Aisino2");
//添加的时候相同的元素,可以添加,但是多个相同的元素,只能存储一份
s.add("Aisino1");
//迭代器遍历
Iterator<String> it = s.iterator();
while(it.hasNext()){
System.*out*.println(it.next());
}
//增强for遍历
for (String s1 : s) {
System.*out*.println(s1);
}
}
}
HashSet
1、该类型也是没有特有的方法,相同元素只存储一份,通过哈希值存储
2、存储自定义类的时候,默认是可以存储相同的元素的,如果想要保证自定义类元素存储的唯一性,需要重写hashCode和equals方法
3、元素存储的运算过程:
(1) 计算要存入元素的hashCode的值,按照哈希值存储,hashCode重写之前就是地址值,重写之后,是根据对象的成员变量的值,产生的一个数字,该数字相同的属性则产生的数字相同,也被称为哈希值
(2) 和当前集合中其他元素进行较,判断是否有相同哈希值的元素
(3) 如果没有相同就可以进行存储
(4) 如果有,需要调用重写之后equals方法判断两个对象中的属性值是否相同,如果相同则不能存储。不同的属性值计算出的哈希值有可能一样,只是概率比较小
(5) 如果equals方法返回false,就意味着属性值不同,只是哈希值相同了,就可以存储
(6) 如果equals方法返回true,就意味着哈希值相同,属性值相同,就认为是同一个对象不可以存储
4、总结:想要保证元素唯一性,就需要在自定义类中重写hashCode和equals方法
数据结构之哈希表
1、哈希值简介:jdk根据对象的地址或者字符串或者数字计算出了的int类型的数值
2、获取哈希值的方式,根据Object类中的hashCode方法,返回对象的哈希值
3、哈希值的特点:
(1) 同一个对象多次调用hashCode方法,返回的hashCode值是相同的
(2) 默认情况下,不同对象的哈希值是不同的,而重写hashCode方法可以实现让不同的对象哈希值相同
4、哈希表结构
(1) jdk1.8之前,数组+链表的形式
(2) jdk1.8之后
① 结点个数小于等于8:数组+链表
② 结点个数大于8:数组+红黑树
Map
简介
1、双列集合的顶层接口
2、Map: 地图,地图上的每一个点,都表示了一个生活中的具体的位置,地图上的点和生活中的位置,有一个一一对应的关系,这种关系叫做映射
3、数据结构:描述的是一个数据(key) 到另外一个数据(value)的映射关系(对应关系)
4、Map<K,V>的特点:称为键值对 描述映射关系
(1) key(键) 是唯一的(不能重复) value(值) 不唯一的
(2) 每个键都能确定一个与之对应的值
5、Map集合中没有索引,所以存储的元素不能保证顺序
Map常用的子类
1、HashMap是常用的子类
2、HashMap<K,V>存储数据采用哈希表结构,元素的存储和取出顺序不能保证一致,由于键是唯一的,所以存储自定义类元素的时候,也要重写hashCode和equals
3、常用的方法:
(1) V put(K key,V value):
① 如果添加的key在Map集合中不存在,那么put方法将键值对添加到map集合中,返回值为null
② 如果添加的key值在集合中存在,那么put方法将修改key对应的value值,返回值就是修改之前的value
(2) V remove(Object key):把指定的键所对应的键值对从Map集合中删除,返回被删除元素的值
(3) V get(Object key):根据指定的键,在Map集合中获取对应的值
(4) boolean containsKey(Object key):判断集合中是否包含指定的键
(5) clear():清空集合
(6) isEmpty() 判断是否为空
(7) size():获取Map集合中键值对的格式
代码
public class Demo07 {
public static void main(String[] args) {
HashMap<String, String> map = npublic class Demo05 {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put("name","金");
map.put("age","30");
map.put("sex","男");
System.out.println(map.put("height", "100"));
System.out.println(map);
//如果添加的key集合中已经存在,则进行修改,返回修改之前的值
System.out.println(map.put("sex", "女"));
System.out.println(map);
//根据对应的键获取对应的值
System.out.println(map.get("height"));
//判断集合中是否包含某个键
System.out.println(map.containsKey("name"));
//判断是否包含某个值
System.out.println(map.containsValue("女"));
// //清空集合的元素
// map.clear();;
// //判断集合是否为空
// System.out.println(map.isEmpty());
// //集合中键值对的个数
// System.out.println(map.size());
//删除集合中的元素 remove 会返回被删除键对应的值
System.out.println(map.remove("name"));
}
}
Map集合的遍历方式
1、通过键遍历,有一个keySet()方法可以将双列集合中所有的key值取出来,放入到Set集合中
2、获取Map集合中的所有键
(1) Set keySet();
3、遍历Set集合的两种方法
(1) 迭代器
(2) 增强for
4、拿到每个键之后,获取对应的值
(1) V get(K key)
代码
public class Demo06 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("name", "金");
map.put("age", "30");
map.put("sex", "男");
map.put("height", "100");
/* Map.Entry是为了更方便的输出map键值对。一般情况下,要输出Map中的key 和 value 是先得到key
的集合keySet(),然后再迭代(循环)由每个key得到每个value。values()方法是获取集合中的所有值,不
包含键,没有对应关系。而Entry可以一次性获得这两个值。*/
1、通过Map集合的keySet()方法,获取所有的
键值(Key那一列)放入到一个Set集合中
Set<String> k = map.keySet();
//2、此时Set集合中存储了所有Map集合中的键,遍历一个一个获取键
Iterator<String> it = k.iterator();
//迭代器遍历
while(it.hasNext()){
//获取的是每一个Set集合的元素 也就是对应的键值
String key = it.next();
//3、通过Key值去寻找对应的value值 map集合中有get方法 获取对应的值
String value = map.get(key);
System.out.println(key + "--->" + value);
}
//增强for循环遍历
for (String key : k) {
String value = map.get(key);
System.out.println(key + "--->" value);
}
}
}
键值对遍历
1、获取Map集合中的,所有键值对对象(Entry),以Set集合的形式返回,使用map集合的entrySet()方法可以获取所有的键值对对象
2、遍历包含键值对对象的Set集合,得到每一个键值对(Entry 对象)
3、通过键值对对象的getKey() 和getValue() 方法获取该键值对的键与值
代码
public class Demo07 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("name", "肖龙");
map.put("age", "30");
map.put("sex", "非女");
map.put("height", "100");
//1、获取键值对的Set集合
Set<Map.Entry<String, String>> set = map.entrySet();
//2、遍历集合
//增强for
for (Map.Entry<String, String> e : set) {
//3、此时e就是每一个键值对对象
//4、通过键值对对象的getKey getValue
String key = e.getKey();
String value = e.getValue();
System.out.println(key + "-->" + value);
}
//迭代器遍历
Iterator<Map.Entry<String, String>> it = set.iterator();
while(it.hasNext()){
Map.Entry<String, String> entry = it.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-->" + value);
}
}
}
HashMap中的key值唯一的分析
1、如果是jdk提供的类型的键,此时可以保证元素唯一性
2、如果存储的是自定义的类型,无法保证元素唯一性,想要保证元素唯一性,需要在自定义类中重写hashCode和equals方法
3、HashMap和HashSet的关系
(1) HashSet是由HashMap实现出来的,HashSet就是HashMap中的键那一列
(2) 将HashMap中值得一列隐藏起来,就变成了HashSet
可变参数
1、参数个数可变,用作方法的形式参数,那么方法的参数的个数就是可变的了,如果方法的参数类型确定,个数不确定,我们就可以使用可变参数
2、格式:
(1) 修饰符 返回值类型 方法名(参数类型...变量名){
可变参数表示参数个数0~n
}
3、注意事项:
(1) 可变参数在方法中其实是一个数组
(2) 如果一个方法有多个参数,包含了可变参数,可变参数必须放在最后
(3) 方法中只能有一个可变参数
Collections工具类
1、Collections是一个单列集合的工具类,在类中封装了很多常用的操作集合的方法,因为该类没有提供构造方法,因此不能创建对象,类中的方法都是静态方法,通过类名调用
2、常用方法:
(1) sort(List list):将列表按照升序排列,从小到大
(2) max、min(Collection c): 获取集合的最大值 最小值
(3) replaceAll(List list,E oldValue,E newValue):将list集合中的老元素替换为新元素
(4) reverse(List list): 将参数list集合进行反转
(5) shuffle(List list): 将list集合中的元素进行随机置换
代码
public class Demo09 {
public static void main(String[] args) {
ArrayList<Integer> list= new ArrayList<>();
list.add(1);
list.add(8);
list.add(8);
list.add(8);
list.add(2);
list.add(3);
*//现在此时集合中是乱序的**
* *//Collections 工具类的方法**
* *//sort排序 自然升序排序**
* System.*out*.println(list);
Collections.*sort*(list);
public class Demo09 {
public static void main(String[] args) {
ArrayList<Integer> list= new ArrayList<>();
list.add(1);
list.add(8);
list.add(8);
list.add(8);
list.add(2);
list.add(3);
//现在此时集合中是乱序的
//Collections 工具类的方法
//sort排序 自然升序排序
System.out.println(list);
Collections.sort(list);
//获取集合中的最大值最小值maxmin
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
System.out.println(list);
//将集合中老的元素替换为新的元素replaceAll
Collections.replaceAll(list,8,6);
System.out.println(list);
//反转reverses
Collections.reverse(list);
System.out.println(list);
//随机打乱元素顺序shuffle
Collections.shuffle(list);
System.out.println(list);
}
}