1、集合概述
1.1、什么是集合?有什么用?
数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。
集合为什么说在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。
在实际开发中,假设连接数据库,数据库当中有10条记录,
那么假设把这10条记录查询出来,在java程序中会将10条
数据封装成10个java对象,然后将10个java对象放到某一个
集合当中,将集合传到前端,然后遍历集合,将一个数据一个
数据展现出来。
1.2、集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,
集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
list.add(100); //自动装箱Integer
注意:
集合在java中本身是一个容器,是一个对象。
集合中任何时候存储的都是“引用”。
1.3、在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中
存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构?数据存储的
结构就是数据结构。不同的数据结构,数据存储方式不同。例如:
数组、二叉树、链表、哈希表…
以上这些都是常见的数据结构。
你往集合c1中放数据,可能是放到数组上了。
你往集合c2中放数据,可能是放到二叉树上了。
…
你使用不同的集合等同于使用了不同的数据结构。
你在java集合这一章节,你需要掌握的不是精通数据结构。java中已经将数据结构
实现了,已经写好了这些常用的集合类,你只需要掌握怎么用?在什么情况下选择
哪一种合适的集合去使用即可。
new ArrayList(); 创建一个集合,底层是数组。
new LinkedList(); 创建一个集合对象,底层是链表。
new TreeSet(); 创建一个集合对象,底层是二叉树。
…
1.4、集合在java JDK中哪个包下?
java.util.*;
所有的集合类和集合接口都在java.util包下。
1.5、为了让大家掌握集合这块的内容,最好能将集合的继承结构图背会!!!
集合整个这个体系是怎样的一个结构,你需要有印象。
1.6、在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
一类是以键值对儿的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
new ArrayList(); 创建一个集合,底层是数组。
new LinkedList(); 创建一个集合对象,底层是链表。
new TreeSet(); 创建一个集合对象,底层是二叉树。
2.继承结构图
一.集合继承结构图
二.Map集合继承结构图
3.Collection接口常用方法
关于java.util. Collection接口中常用的方法。
1.Collection中能存放什么元素?
没有使用泛型之前, Collection中可以存Object的所有子类型。
使用泛型之后, Collection中只能存情某个具体的类型
集合后期我们会学泛型语法。日前先不用管。 Collection中什么都能存
只要是Objec的子类型就行。(集合中不能存储基本数据类型,也不能存java对象,只存储java对象的内存地址)
2、 Collection中的常用方法
boolean add(Object e)向集合中添加元素
package com.javase.collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest01 {
public static void main(String[] args) {
Collection collection =new ArrayList();
collection.add(100);//自动装箱,实际是放进去了一个对象的内存地址
collection.add(new Object());
collection.add(true);//自动装箱
}
}
2、 Collection中的常用方法
boolean add(Object e) 向集合中添加元素
int size() 获取集合中元素的个数
void clear() 清空集合
boolean contains(Object o)判断当前集合中是否包含元素o,包含返回true,不包含返回false
boolean remove(Object o)删除集合中的某个元素。
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest01 {
public static void main(String[] args) {
//创建一个集合对象
// Collection c= new Collection();/接口是抽象的,无法实例化。
//多态
Collection collection =new ArrayList();
//测试Collection接口中的常用方法
collection.add(100);//自动装箱(java5的新特性),实际上是放进去了一个对象的内存地址。 Integer x= new Integer()
collection.add(new Object());
collection.add(true);
collection.add(new Student());
System.out.println(collection.size());
//清空集合
collection.clear();
System.out.println(collection.size());
collection.add("lupeng");
collection.add("yinying");
collection.add("ying");
collection.add(1);
//判断集合中是否包含lupeng
boolean flay=collection.contains("lupeng");
System.out.println(flay);
boolean flay2=collection.contains("zhang");
System.out.println(flay2);
//获取集合中元素的个数
System.out.println("删除之前的size="+collection.size());
collection.remove("yinying");
System.out.println("删除之后的size="+collection.size());
//先判断集合是否为空,清空后再判断一次
boolean flag1=collection.isEmpty();
System.out.println("是否为空前"+flag1);
collection.clear();
boolean flag2=collection.isEmpty();
System.out.println("是否为空后"+flag2);
collection.add("lupeng");
collection.add(730);
collection.add("ying");
//将集合转换数组(了解)
Object[] obj=collection.toArray();
for (int i=0;i<obj.length;i++){
Object o=obj[i];
System.out.println(o);
}
}
}
class Student{
}
4.Collection集合迭代
一.实例
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest02 {
public static void main(String[] args) {
//注意:以下讲解的遍历方式/迭代方式,是所有 Collection通用的一种方式。
//在Map集合中不能用。在所有的 Collection以及子类中使用。
//创建集合对象
Collection collection=new ArrayList();
collection.add("abc");
collection.add(1314);
collection.add("def");
collection.add(new Object());
collection.add(1314);
//对集合 Collection进行遍历/迭代
//第一步:获取集合对象的迭代器对象Iterator
Iterator it= collection.iterator();
/*
第二步:通过以上获取的迭代器对象开始迭代/遍历集合。
以下两个方法是迭代器对象Iterator中的方法
boolean hasNext()如果仍有元素可以迭代,则返回true
Object next()返回迭代的下一个元素。*/
while(it.hasNext()){
//未规定泛型,默认都是object类型
System.out.println(it.next());
//只不过在输出的时候会转换成字符串,因为这里println会调用toString()方法
}
}
}
abc
1314
def
1314
java.lang.Object@723279cf
同时说明了ArrayList集合有序且可以重复
二.原理图
原理演示,但注意集合里面存储都是对象的内存地址,写上abc 、def只是方便演示
三.集合方法解析
1.contains方法解析
深入Collection集合的 contains方法
boolean contains(Object o)
判断集合中是否包含某个对象o
如果包含返回true,如果不包含返回 false
contains方法是用来判断集合中是否包含某个元素的方法,那么它在底层是怎么判断集合中是否包含某个元素的呢
调用了 equals方法进行比对。
equals方法返回true,就表示包含这个元素。
String类已经重写了equals方法所以是true
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest04 {
public static void main(String[] args) {
Collection c=new ArrayList();
String s1=new String("abc");
String s2=new String("def");
String x=new String("abc");
c.add(s1);
c.add(s2);
System.out.println(c.size());
System.out.println(c.contains(x));
}
}
2
true
内存图
2.equlas方法和remove方法对比
//package com.javase.collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest05 {
public static void main(String[] args) {
Collection c=new ArrayList();
User u1=new User("jack");
c.add(u1);
User u2=new User("jack");
System.out.println(c.contains(u2));
//未重写equals方法,所以false
//重写后true
c.remove(u2);
//未重写equals方法,size为1
//重写后0
System.out.println(c.size());
Collection c2=new ArrayList();
String s1=new String("hellow");
String s2=new String("hellow");
c2.add(s1);//s1.equals(s2) java认为s1和s2是一样的。删除s1就是删除s2
c2.remove(s2);
//集合中元素的个数
System.out.println(c2.size());
}
}
class User{
private String name;
public User(){}
public User(String name){
this.name=name;
}
public boolean equals(Object obj){
if(obj==null||!(obj instanceof User)) return false;
User user=(User)obj;
if (name == user.name){
return true;
}
return false;
}
}
true
0
0
3.关于集合中元素的删除remove()
关于集合元素的remove
重点:当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的送代器,会出现
7: java util. ConcurrentModificationException
重点:在送代集合元素的过程中,不能调用集合对象的 remove方法,删除元素
c, remove(o);迭代过程中不能这样
会出现:java.util.ConcurrentModificationException
重点:在送代元素的过程当中,一定要使用送代器 Iterator的 remove方法,删除元素
不要使用集合自带的remove方法删除元素。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest06 {
public static void main(String[] args) {
Collection c=new ArrayList();
/*
注意:此时获取的迭代器,指向的是那是集合中没有元素状态下的送代器。
一定要注意:集合结构只要发生改变,迭代器必须重新获取。
当集合结构发生了改变,送代器没有重新获取时,调用next()方法时:java.utit.ConcurrentModificatiolException*/
/* Iterator it2=c.iterator();
c.add(1);
c.add(2);
c.add(3);
Iterator it=c.iterator();
while(it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}*/
Collection c2=new ArrayList();
c2.add("ying");
c2.add("lupeng");
c2.add("like");
Iterator it=c2.iterator();
while(it.hasNext()){
Object obj=it.next();
//c2.remove(obj); error
/* //删除元素
//删除元素之后,集合的结构发生了变化,应该重新去获取送代器
//但是,循环下一次的时候并没有重新获成送代器,所以会出现异常:java.util.ConcurrentModificationException
//出异常根本原因是:集合中元素删除了,但是没有更新送代器(迭代器不知道集合交化了)
//c2. remove(obj);/直接通过集合去删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同.)
//使用迭代器来删除可以吗?
迭代器去删除时,会自动更新迭代器,并且更新集合(删除集合中的元素)*/
it.remove();//删除的一定是迭代器指向的当前元素
System.out.println(obj);
}
System.out.println(c2.size());
}
}
ying
lupeng
like
0
4.List接口特有方法
测List接口中常用方法
1、List集合存储元素特点:有序可重复
有序:List集合中的元素有下标。
从0开始,以1递增。
可重复:存储一个1,还可以再存储1
2、List既然是 Collection接口的子接口,那么肯定List接囗有自己特色的方法
以下只列出List接口特有的常用的方法
void add (int index, Object element)
Object get(int index)
int indexof(Object o)
int lastIndexof(Object o)
Object remove(int index)
Object set(int index, Object element)
import java.util.ArrayList;
import java.util.Iterator;
public class ListTest01 {
public static void main(String[] args) {
//创建List类型的集合。
//List mylist new Linkedlist();
//List mylist new Vector();
ArrayList myList=new ArrayList();
//添加元素
myList.add(1);//默认都是向集合末尾添加元素
myList.add("A");
myList.add("B");
myList.add("B");
myList.add("c");
//这个方法用的不多,因为对ArrayList集合说效率比较低
myList.add(1,"ying"); //在列表的指定位置插入指定元素(第一个参数是下标)
//迭代
Iterator it=myList.iterator();
while(it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
System.out.println("=======================================");
//根据下标获取元素
Object obj=myList.get(0);
System.out.println(obj);
System.out.println("=============");
//因为有下标,所以List集台有自己比较特殊的遍历方式
//通过下标遍历.List集合特有的方式,Set没有。
for (int i=0;i<myList.size();i++){
System.out.println(myList.get(i));
}
//获取指定对象第一次出现处的索引
System.out.println(myList.indexOf("B"));
//获取指定对象最后一次出现处的索引。
System.out.println(myList.lastIndexOf("B"));
//删除指定下标位置的元素
//删除下标为0的元素
myList.remove(0);
//修改指定位置的元素
myList.set(1,"lupeng");
System.out.println("===============");
//遍历集合
for (int i=0;i<myList.size();i++){
System.out.println(myList.get(i));
}
}
}
1
ying
A
B
B
c
+++++++++++++++++++++++++++++
1
+++++++++++++++++++++++++++++++++++
1
ying
A
B
B
c
3
4
++++++++++++++++++++
ying
lupeng
B
B
c
一.ArrayList集合
1.概述
1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)
2、集合底层是一个Object[]数组。
构造方法
new Arraylist;
new Arraylist(20)
4 arraylist集合的扩容
原容量的1.5倍
ArrayList集合底层是数组,怎么优化?
尽可能少的扩容。因为数组扩容效率比较低,建议在使用 ArrayList集合
的时候预信计元素的个数,给定一个初始化容量。
5、数组优点:
检索效率比较高。(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。)
6、数组缺点:
随机增删元素效率比较低。
7、向数组末尾添加元素,效率很高,不受影响。
8、面试宫经常问的一个问题?
这么多的集合中,你用哪个集合最多?
答: Arraylist集合。
因为往数组末尾添加元素,效率不受影响。
另外,我们检索/查找某个元素的操作比较多。
public class ArrayListTest01 {
public static void main(String[] args) {
List list=new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
list.add(8);
list.add(9);
list.add(10);
System.out.println(list.size());
/* int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, *//* minimum growth *//*
oldCapacity >> 1 *//* preferred growth *//*);*/
list.add(11);
System.out.println(list.size());
}
}
10
11
2.ArrayList集合另一个构造方法
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class ArrayListTest02 {
public static void main(String[] args) {
List myList1=new ArrayList();
//用指定初始容量构造一个空列表
//初始化容量为10
//第二种带参数的构造方法初始化容量为100
List myList2=new ArrayList(100);
Collection c =new HashSet();
//创建一个 Hashset集合
c.add(10);
c.add("ying");
c.add("lu");
//通过这个构造方法就可以将HashSet集合转换成List集合。将haseset集合传进去就是下面c
List myList3=new ArrayList(c);
for (int i=0;i<myList3.size();i++){
System.out.println(myList3.get(i));
}
}
}
ying
lu
10
二.LinkedList源码分析
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class LinkListTest01 {
public static void main(String[] args) {
List list=new LinkedList();
list.add("a");
list.add("b");
list.add("c");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
// Linkedlist集合有初始化容量吗?没有。
//最初这个链表中没有任何元素。 first和last引用都是 null
//不管是 Linkedlist还是 Arraylist,以后写代码时不需要关心具体是哪个集合。
//因为我们要面向接口编程,调用的方法部是接口中的方法。
List list1=new ArrayList();//这样写表示底层用了数组
List list2=new LinkedList();//这样写表示底层用了双向链表
}
}
三.Vector集合源码解析
Vector
1、底层也是一个数组。
2、初始化容量:10
3、怎么扩容的?
扩容之后是原容量的2倍
10–)>20–)40–>80
4、 Arraylist集合扩容特点:
ArrayList容量扩容为原来两倍
5、 Vector中所有的方法都是线程同步的,都带有 synchronized关键字
是线程安全的。效率比较低,使用较少了。
6、怎么将一个线程不安全的 realist集合转换成线程安全的呢?
使用集合工具类
java util. Collections
java.utiL. Collection是集合接口
java.utiL. Collections是集台工具类。
import java.util.*;
public class VectorTest {
public static void main(String[] args) {
Collection vector=new Vector();
vector.add(1);
vector.add(2);
vector.add(3);
vector.add(4);
vector.add(5);
vector.add(6);
vector.add(7);
vector.add(8);
vector.add(9);
vector.add(10);
//满容后扩容2倍
vector.add(11);
Iterator it=vector.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
Collection mylist=new ArrayList();//非线程安全
Collections.synchronizedCollection(mylist);//变成线程安全
}
}
5.泛型
一.概述
1、JDK5.0之后推出的新特性:泛型
2、泛型这种语法机制,只在程序编泽阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)
3、使用了泛型好处是什么?
第-:集合中存信的元素类型统-了
第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的向下转型!
4、泛型的缺点是什么?
导致集合中存的元素缺乏多样性
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class genericTest01 {
public static void main(String[] args) {
/* List myList=new ArrayList();
Cat c=new Cat();
Bird b=new Bird();
myList.add(c);
myList.add(b);
Iterator it=myList.iterator();
while (it.hasNext()){
//没有这个语法,通过迭代器取出的就是Obect
Object obj=it.next();
if(obj instanceof Animal){
//obj中没用move方法,无法调用,需要向下转型
Animal a=(Animal)obj;
a.move();
}
}*/
//使用JDK5之后的泛型机制
//使用泛型List<Animal>之后,表元List集合中只允许存Animal类型的数据。
// 用泛型来指定集合中存储的数据类型。
//这样使用泛型之后,表示list集合只允许存储Animal类型的数据
List<Animal> myList1=new ArrayList<Animal>();
// 指定ist集合中只能存 Animal,那么存体 tring就编译报错了
//这样用了泛型之后,集合中元素的数据类型更加统-了。
/*myList1.add("abc");*/
Cat c=new Cat();
Bird b=new Bird();
myList1.add(c);
myList1.add(b);
//获取迭代器
//这个表示迭代器迭代的是Animal类型
Iterator<Animal> it=myList1.iterator();
while(it.hasNext()){
//使用泛型之后,每一次送代返回的数据都是 Animal类型。
//Animal a= it. next();
//这里不需要迸行强制类型转换了。接调用。
//a move();
//调用子类型特有的方法还是需要向下转换的!
Animal a=it.next();
if (a instanceof Cat){
Cat c1=(Cat)a;
c1.CatchMouse();
}else if(a instanceof Bird){
Bird b1=(Bird)a;
b1.fly();
}
}
}
}
class Animal{
public void move(){
System.out.println("动物在移动");
}
}
class Cat extends Animal{
public void CatchMouse(){
System.out.println("猫抓老鼠");
}
}
class Bird extends Animal{
public void fly(){
System.out.println("鸟在飞翔");
}
}
猫抓老鼠
鸟在飞翔
二.自定义泛型
自定义泛型可以吗?可以
自定义泛型的时候,<>尖括号中的是一个标识符,随使写。
java源代码中经常出现的是
和
E是 ELement单词首字。
T是Type单词首字。
public class genericTest03<标识符随便写> {
public void doSome(标识符随便写 o){
System.out.println(o);
}
public static void main(String[] args){
//new对象的时候指定了泛型是:String类型
genericTest03<String> gt=new genericTest03<>();
gt.doSome("abc");
//gt.doSome(100); 类型不匹配
System.out.println("++++++++++++++++++");
genericTest03<Integer> gt2=new genericTest03<>();
gt2.doSome(100);
System.out.println("================================");
MyIterator<String> m1=new MyIterator<>();
String s1=m1.get();
MyIterator<Animal> m2=new MyIterator<>();
Animal a=m2.get();
//不用泛型是Object类型
genericTest03 gt3=new genericTest03();
gt3.doSome(new Object());
}
}
class MyIterator<E>{
public E get(){
return null;
}
}
abc
++++++++++++++++++
100
6.foreach遍历集合
一.语法
增强for( foreach)
以下是语法
for(元素类型 变量名:数组或集合){
System.out. println(变量名);
}
1.数组
}
public class ForEachTest01 {
public static void main(String[] args) {
int[] arr={12,24,53,232,43};
for (int i=0;i< arr.length;i++){
System.out.println(arr[i]);
}
System.out.println("+++++++++++++++++++");
//foreach有一个缺点:没有下标
for (int data:arr){
//data就是数组中的元素(数组中的每一个元素)
System.out.println(data);
}
}
}
12
24
53
232
43
+++++++++++++++++++
12
24
53
232
43
2.集合
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ForEachTest02 {
public static void main(String[] args) {
List<String> strlist=new ArrayList<>();
strlist.add("lupeng");
strlist.add("come on");
strlist.add("never give up");
//遍历方式,使用迭代器遍历
Iterator<String> it=strlist.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//使用下标方式(只针对有下标的集合)
for(int i=0;i<strlist.size();i++){
System.out.println(strlist.get(i));
}
//使用foreach
for(String s:strlist){ //因为泛型使用的是String类型,所以是String s
System.out.println(s);
}
}
}
lupeng
come on
never give up
lupeng
come on
never give up
lupeng
come on
never give up
7.简单演示HashSet和TreeSet
一.HashSet
无序不重复集合
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetTest01 {
public static void main(String[] args) {
//遍历
/* hello1
hello3
hello5
hello4
1、存时顺序和取出的顺序不同。
2、不可重复
3、放到ash5et集合中的元素实际上是放到HashMap*/
Set<String> str=new HashSet<>();
str.add("fuck1");
str.add("fuck3");
str.add("fuck5");
str.add("fuck4");
str.add("fuck3");
Iterator<String>it =str.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
str.add("1");
str.add("2");
str.add("fuck1");
str.add("1");
//使用增强for
for(String s:str){
System.out.println(s);
}
}
}
fuck3
fuck4
fuck5
fuck1
1
2
fuck3
fuck4
fuck5
fuck1
TreeSet
Treeset集台存元素特点:
1、无序不可重复的,但是储存的元素可以自动按照大小顺序排序!
称为:可排序集合。
2.无序:这里无序指的是存进去的顺序和取出来的顺序不同。并且没有下标。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class TreeSetTest01 {
public static void main(String[] args) {
Set<String> hashset=new HashSet<>();
hashset.add("A");
hashset.add("M");
hashset.add("C");
hashset.add("F");
hashset.add("C");
Iterator<String> it =hashset.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
A
C
F
M