------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1:集合
1、集合的概念:java是一种面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能
2、集合的特点:
1、长度可以发生改变
2、只能存储对象
3、可以存储多种类型对象(一般存储的还是同一种)
3、集合和数组的区别
A:长度问题
数组固定
集合可变
B:存储元素问题
数组可以是基本类型,也可以是引用类型。
集合只能是引用类型。
C:是否同一类型
数组元素类型一致。
集合元素类型可以不一致。
4/、集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,java就提供了多种集合类。而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系结构。
数据结构:数据存储的方式。
程序 = 算法 + 数据结构
Collection
|--List (有序的 collection(也称为序列))
|--ArrayList (List 接口的大小可变数组的实现。)
|--Vector (Vector 类可以实现可增长的对象数组)
|--LinkedList (List 接口的链接列表实现)
|--Set
|--HashSet
|--TreeSet
5、集合的框架构成
二、Collection的功能
(1)Collection的功能
1、添加功能
booleanadd(Object obj):向集合中添加一个元素。
booleanaddAll(Collection c):向集合中添加一个集合的元素。
2、删除功能
voidclear():删除集合中所有的元素。
booleanremove(Object obj):删除集合中指定的元素。
booleanremoveAll(Collection c):删除集合中指定的集合元素。只要有数据删除,则返回true。
3、判断功能
booleanisEmpty():判断集合是否为空。
booleancontains(Object obj):判断集合是否包含指定的元素。
booleancontainsAll(Collection c):判断集合是否包含指定的集合中的元素。只有所有数据包含了,才返回true。
4、遍历功能
A:Iteratoriterator():迭代器。
hasNext():判断是否还有元素
next():获取下一个元素
B:增强for循环遍历集合。
5、长度功能
intsize():获得集合的元素个数。
6、交集功能
booleanretainAll(Collection c):判断集合中是否有相同的元素。
如果有两个集合A和B。A对B做交集。
A集合保存的是交集元素,B集合不发生改变。
返回值表示的是A集合是否发生过改变。
7、转换功能
Object[]toArray():把集合变成数组。
(2)迭代器的使用
1、使用步骤
1、通过集合对象获取迭代器对象。
2、通过迭代器对象判断。
3、通过迭代器对象获取。
2、迭代器原理
由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。
这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种
集合的时候,只要该集合内部实现这个接口即可。
3、迭代器源码
publicinterface Iterator
{
public abstract boolean hasNext();
public abstract Object next();
}
publicinterface Collection
{
public abstract Iterator iterator();
}
publicinterface List extends Collection
{
...
}
publicclass ArrayList implements List
{
public Iterator iterator()
{
returnnew Itr();
}
private class Itr implements Iterator
{
publicboolean hasNext(){...}
publicObject next(){...}
}
}
(3)集合的常见使用步骤:
1、创建集合对象
2、创建元素对象
3、把元素添加到集合中
4、遍历集合
1、通过集合对象获取迭代器对象。
2、通过迭代器对象判断。
3、通过迭代器对象获取。
(4)Collection存储字符串和自定义对象并遍历。
1、存储字符串
Collectionc = new ArrayList();
//Strings = "hello";
//c.add(s);
c.add("hello");
c.add("world");
c.add("java");
Iteratorit = c.iterator();
while(it.hasNext())
{
String s = (String)it.next();
System.out.println(s);
}
2、存储自定义对象(自己补齐)
Collection c=newArrayList();
Student s1=newStudent("林青霞",26);
c.add("s1");
Iteratorit=c.iterator();
while(it.hasNext())
{
Strings=(String)it.next();
System.out.println(s);
}
3:List的特有功能
(1)List的特点
List是Collection接口下的一个子接口
特点:元素有序(指的是存储顺序和取出顺序是否一致),可重复。
Set也是Collection接口下的一个子接口
特点:元素无序,唯一。
(1)List的特有功能
1、添加功能
voidadd(int index,Object obj): 在指定位置添加元素。
2、删除功能
Objectremove(int index): 根据指定索引删除元素,并把删除的元素返回
3、修改功能
Objectset(int index,Object obj):把指定索引位置的元素修改为指定的值,返回修改前的值。
4、获取功能
Objectget(int index): 获取指定位置的元素
intindexOf(Object obj): 返回指定元素在集合中第一次出现的索引。
ListIteratorlistIterator():
5、截取功能
ListsubList( int fromIndex, int toIndex )截取集合。
(2)List的遍历方式
1、Iterator迭代器
2、ListIterator迭代器
3、普通for+get()
4、增强for循环遍历
(3)ListIterator迭代器
ConcurrentModificationException并发修改异常
ConcurrentModificationException
①:为什么出现这个异常:因为我们在用迭代器遍历的时候,通过集合对元素进行了操作
②:如何解决呢:
A:通过迭代器遍历的时候,我们用迭代器进行操作集合元素
ListIterator
B:普通for循环遍历集合的时候,我们通过集合对元素进行操作
补充:
A:只有List里有get();collection没有;因为List有序;
B:集合没有length;只有size
List的三个儿子
(1)List的三个儿子特点:
List
|--ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
|--Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
|--LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
(2)例:ArrayList存储自定义对象去重复
需求:ArrayList如果存储的是学生,那么,怎么去除重复元素呢?
问题:如何知道学生是重复的?
需求:如果学生的姓名和年龄相同,我就认为是同一个学生,即重复值。
问题分析: 通过简单分析,我们估计是判断那里出问题了。
怎么办呢?
看判断的方法。
而我们又知道,判断的方法是API提供的,不是自己写的。
所以,看源码,看底层到底是怎么实现的。
通过看原码,我们发现,底层依赖的是equals()方法。
由于学生类中,我们并没有equals()方法,所以,默认用的是Object的方法。
而Object类的方法,默认比较的是地址值。
由于学生对象都是new出来的,地址值肯定不一样,所以从这个角度考虑结论是正确的。
但是不符合我们的需求。
怎么办呢?
重写equals()方法,让它按照我们的需求来比较。
代码:
public static void main(String[] args) {
ArrayListarray = new ArrayList();
Students1 = new Student("郑成功", 40);
Students2 = new Student("戚继光", 50);
Students3 = new Student("戚继光", 50);
Students4 = new Student("岳飞", 36);
Students5 = new Student("岳飞", 40);
Students6 = new Student("林则徐", 30);
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);
array.add(s6);
//创建新集合
ArrayListarray2 = new ArrayList();
//遍历旧集合,获取到每一个元素
Iteratorit = array.iterator();
while(it.hasNext()) {
Student s = (Student) it.next();
// 在新集合中判断,看是否存在这个元素
if (!array2.contains(s)) {
//如果s不再array2中存在,就添加
array2.add(s);
}
}
//array2就是没有重复元素的集合。
//遍历array2
for(int x = 0; x < array2.size(); x++) {
Student s = (Student) array2.get(x);
System.out.println(s.getName() +"***" + s.getAge());
}
}
(3):Vector的特有功能
A:添加功能。
void addElement( Object obj )
B:获取功能。
public Object elementAt( int index )
public Enumeration elements()
类Enumeration里边有两个方法。(类似于迭代器)
boolean hasMoreElements()
Object nextElement()
public int size()
JDK版本升级考虑的因素:A:安全。B:效率。C:简化书写。
(4):LinkedList的特有功能
A:添加功能
void addFirst( Object o ):将指定元素插入此列表的开头。
void addLast( Object o ):将指定元素添加到此列表的结尾。
B:获取功能
Object getFirst()
Object getLast()
C:删除功能
Object removeFirst()
Object removeLast()
(5):例:用LinkedList模拟栈数据结构
packagecn.itcast_02;
A:第一步,自定义栈集合的类。
importjava.util.LinkedList;
//自定义栈集合
publicclass MyStack {
//通过LinkedList模拟栈数据结构
private LinkedList list;
public MyStack()
{
list= new LinkedList();
}
public void add(Object obj)
{
list.addFirst(obj);
}
public Object get(int index)
{
returnlist.get(index);
}
public int size()
{
returnlist.size();
}
}
B:在测试类中测试。
package cn.itcast_02;
public class MyStackTest {
publicstatic void main(String[] args) {
MyStack ms = new MyStack();
ms.add("hello");
ms.add("world");
ms.add("java");
for (int x = 0; x < ms.size(); x++) {
Strings = (String) ms.get(x);
System.out.println(s);
}
}
}
(6)到底使用List还是Vector?
根据需求看是否要安全:
是:Vector
否:ArrayList,LinkedList
查询多:ArrayList
增删多:LinkedList
如果你什么都不知道,用ArrayList。
4:泛型
(1)泛型是一种把明确类型的工作放在了创建对象或者调用方法时候才去明确的特殊的类型。
(2)格式:
<数据类型>
(3)泛型的好处:
A:解决了黄色警告线问题
B:把运行期间的转换异常给提前到了编译期间
C:优化了程序设计,不需要做强制类型转换了
1):泛型类
需求:我能不能不用重载方法,就可以实现同一个方法,输出不同类型的数据呢?
packagecn.itcast_03;
//尖括号中的QQ代表的是任意类型,QQ也可以用其他任意字母代替(自定义)
publicclass Tool {
//这里的QQ必须和上边尖括号中的名字一样,qq是变量名,可以自定义
public void show(QQ qq)
{
//输出QQ类型的变量qq
System.out.println(qq);
}
}
2):泛型方法
你为了保证方法传递不同的参数,你就在类上明确了类型。这样的话,你有没有发现一个问题呢?它要是能够在调用方法的时候,才去明确类型该有多好呢?
publicclass Tool
{
public void show( BYD byd )
{
System.out.println(byd );
}
}
3):泛型接口
A:定义Inter接口的代码。
packagecn.itcast_03;
publicinterface Inter {
public abstract void show(BMW bmw);
}
B:接口Inter的实现类InterImpl中的代码。
packagecn.itcast_03;
publicclass InterImpl implements Inter {
@Override
public void show(BMW bmw) {
System.out.println(bmw);
}
5:增强for循环
(1)格式:
for(数组或者Collection集合的元素类型 变量 : 数组或者Collection集合的对象)
{
直接使用变量即可。
}
(2)好处:
方便了数组和Collection集合的遍历。
(3)注意(注意):
A:增强for是用来替代迭代器的。其底层就是调用的迭代器
B:不要在用增强for遍历集合的时候,用集合对集合本身进行修改。
(4)遍历List集合体系三种方式
1):迭代器
2):普通for+get
3):增强for(工作时候用)
Collection集合遍历两种方式
1):迭代器
3):增强for(工作时候用)
Set集合
Set的特点:
元素无序,唯一。(注意和List的对比:元素有序(数据存储和取出顺序一致),可以重复。)
注意:这里的顺序是指存储和取出顺序。
1:HashSet
(1)HashSet:不保证元素的迭代顺序。并且,不保证该顺序恒久不变。
(2)怎么保证的呢?
HashSet底层数据结构是哈希表。
它依赖两个方法:hashCode()和equals()
顺序:
首先,判断hashCode()值是否相同。
相同:
继续走equals()方法,根据其返回值:
true:说明元素重复,不添加到集合。
false:说明元素不重复,添加到集合。
不同:直接添加到集合。
(3)重写hashCode()和equals()方法
hashCode():
把对象的所有成员变量值相加即可。
如果是基本类型,就加值。如果是引用类型,就加哈希值。
public int hashCode()
{
returnthis.name.hashCode() + this.age + this.sex +this.score ;
}
equals():
A:this==obj
B:!(obj instanceof Student)
C:所有成员变量的值比较。基本类型用==,引用类型用equals()。
如果不会,eclipse()自动生成。
2:TreeSet
(1)TreeSet:根据构造方法的不用,选择使用自然排序或者比较器排序。
按照实际的需求,可以对元素进行排序。并且保证唯一。
(2)怎么保证的呢?
排序:底层结构是二叉树。按照树节点进行存储和取出。
两种实现:
A:自然排序(元素具备比较性)
TreeSet的无参构造,要求对象所属的类实现Comparable接口。
public int compareTo( Studetn s )
{
//需求是比较年龄
intnum = this.age - s.age ;
//由于对象有多个成员变量,你不能根据其中的某一个决定其他的。
//当某一个形同的时候,你还需要判断其他的是不是也是相同的。
intnum2 = ( num == 0 ) ? ( this.name.compareTo( s.name ) ) : num ;
returnnum2;
}
B:比较器排序(集合具备比较性)
TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。
TreeSet ts = newTreeSet ( new Comparator
{
@Override
publicint compare ( Student s1 , Student s2)
{
//按照年龄排序,从小到大
int num = s1.getAge() - s2.getAge();
//次要条件
int num2 = ( num == 0 ) ? (s1.getName().compareTo(s2.getName()) ) : num;
return num2;
}
} );
//创建元素对象
Student s1 = new Student(“张三”,24);
Student s2 = new Student(“李四”,30);
Student s1 = new 。。。。。。
//添加元素
ts.add(s1);
ts.add(s2);
...........
for( Student s : ts )
{
System.out.println(s.getName()+ "*****" + s.getAge());
}
唯一:根据返回值是否为0。
注意:
如果同时有两种方案,以谁为主呢?以比较器为主。
(3)例:TreeSet存储自定义对象按照姓名长度排序
publicint compareTo( Studetn s )
{
//需求是比较姓名的长度
int num = this.name.length() -s.name.length() ;
//很多时候,别人给我们的需求其实只是一个主要需求
//还有很多的次要需求是需要我们自己进行分析的。
//比较姓名的内容
int num2 = ( num == 0 ) ? (this.name.compareTo( s.name ) ) : num ;
//继续分析,姓名长度和内容都相同的情况下,年龄还可能不一样呢。
//所以,当姓名长度和内容都相同的时候,我们在比较下年龄就好了。
int num3 = ( num2 == 0 ) ? ( this.age -s.age ) : num3 ;
return num3;
}
(4)原理:二叉树保证元素唯一排序
A:第一个添加的数据作为根节点。
B:从第二个开始,
每一个数据从根节点开始比较,
如果大了,往右边放,
如果小了,往左边放,
如果相同,替换。
C:从根节点开始,获取数据的规则,是按照每个数据的:左,中,右原则。
4:Collection体现的集合总结
Collection
|--List
|--ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高。
|--LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高。
|--Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低。
|--Set唯一
|--HashSet
底层数据结构是哈希表。
如何保证元素唯一性呢?
依赖两个方法。hashCode()和equals()。
以后都自动生成。
|--TreeSet
底层数据结构是二叉树。
如何保证元素唯一性呢?如何保证元素排序呢?
根据返回值是否是0,判断元素是否重复。
排序有两种方案:
元素具备比较性实现Comparable接口
集合具备比较性实现Comparator接口
5:在集合中的数据结构问题
ArrayXxx:底层数据结构是数组。查询快,增删慢。
LinkedXxx:底层数据结构是链表。查询慢,增删快。
HashXxx:底层数据结构是哈希表。跟两个有关。hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种排序方式。Comparable接口和Comparator接口
6:什么时候,使用哪种Collection集合。
元素唯一吗?
唯一:
Set
需要排序吗?
需要:TreeSet
不需要:HashSet
不知道,用HashSet。
不唯一:
List
需要安全码?
需要:Vector
不需要:ArrayList和LinkedList
查询多:ArrayList
增删多;LinkedList
不知道,用ArrayList。
7:Collections
(1)Collections是针对Collection集合操作的工具类。
publicstatic void sort ( List list ):排序。
publicstatic
publicstatic void reverse( List list ):反转。
publicstatic T max( Collection coll ):最大值。
publicstatic void shuffle( List list ):随机置换 (相当于“洗牌”)
(2)Collection和Collections的区别
Collection:是Collection集合的顶层接口,定义了Collection集合的共性方法。
Collections:是一个类,定义了针对Collection集合操作的功能,有排序,查找,反转等。
(3)案例:斗地主洗牌发牌小游戏
publicstatic void main(String[] args) {
// 买牌
// 表示花色的数组
String[] colors = { "黑桃", "红桃", "梅花", "方块" };
// 表示点数的数组
String[] numbers = { "A","2", "3", "4", "5", "6","7", "8", "9", "10",
"J", "Q","K" };
// 早一个牌盒
ArrayList array = newArrayList();
array.add("大王");
array.add("小王");
// 循环装牌
for (String c : colors) {
for(String n : numbers) {
array.add(c.concat(n));
}
}
// 显示所有牌
// System.out.println(array);
// 洗牌
Collections.shuffle(array);
// 显示所有牌
// System.out.println(array);
// 发牌
ArrayList linString = newArrayList();
ArrayList zhouString = newArrayList();
ArrayList meString = newArrayList();
// 用普通for
for (int x = 0; x < array.size() - 3;x++) {
if(x % 3 == 0) {
linString.add(array.get(x));
}else if (x % 3 == 1) {
zhouString.add(array.get(x));
}else if (x % 3 == 2) {
meString.add(array.get(x));
}
}
// 看牌
System.out.println("linString:"+ linString);
System.out.println("zhouString:"+ zhouString);
System.out.println("meString:"+ meString);
// 看底牌
for (int x = array.size() - 3; x
三、Map集合
(1)Map是一个键值对形式的集合。它的元素都是有键和值组成。
(2)Map和Collection的区别?(面试题)
A:Map 是由键值对组成的集合,Map的键(key)是唯一的,值(value)可以重复。
B:Collection 是有单列数据组成的集合,它的儿子List是可以重复的,Set是唯一的。
(3)HashMap和Hashtable的区别?(面试题)
HashMap:线程不安全,效率高。允许null键和值。
Hashtable:线程安全,效率低。不允许null键和值。
(4)Map的功能:
A:添加功能
V put(K key, V value) 当key在集合中不存在时,添加元素;
当key在集合中存在的时候,替换元素。
B:判断功能
booleancontainsValue( Object value ) 判断指定的值是否在集合中存在。
booleanisEmpty() 判断集合是否为空。
C:删除功能
voidclear() 清除所有键值对数据。
Vremove( Object key ) 根据指定的键删除键值对。
D:获取功能
Set
Objectget( Object key ):根据键获取值。
Set
Collection
E:长度功能
intsize()
(5)Map的两种遍历方式
A:丈夫找妻子(掌握)
a:把所有丈夫给集合起来。Set
b:遍历丈夫集合,获取到每一个丈夫。增强for , 迭代器
c:让丈夫去找妻子。get(Object key)
//创建集合对象
Map map = new HashMap();
//往集合里边添加元素
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
map.put("牛郎", "侄女");
map.put("萧炎", "萧薰儿");
//方式1 遍历,"丈夫找妻子"
//得到所有的丈夫对象
SethusbandSet = map.keySet();
//用迭代器的方式来做
Iteratorit = husbandSet.iterator();
while(it.hasNext())
{
String husband = (String)it.next();
String wife = map.get(husband);
System.out.println(husband +"-----"+wife);
}
//用增强for来做
for(String s : husbandSet) {
// 根据“丈夫”找到“妻子”--根据键获取值
String wife = map.get(s);
System.out.println(s + "******"+ wife);
}
B:根据结婚证找丈夫和妻子
a:获取所有结婚证的集合。Set<> entrySet()
b:遍历结婚证集合,获取到每一个结婚证对象。迭代器,增强for
c:通过结婚证对象获取丈夫和妻子getKey()和getValue()
// 创建集合对象
Map map = new HashMap();
//往集合里边添加元素
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
map.put("牛郎", "侄女");
map.put("萧炎", "萧薰儿");
//找到所有的“结婚证”,也就是所有的键值对
Set> set = map.entrySet();
//方式2 遍历,这样每次遍历我都会得到一个键值对
for(Map.Entry en : set) {
// 得到键和值,然后打印
String key = en.getKey();
String value = en.getValue();
System.out.println(key + "****"+ value);
}
2:HashMap
(1)HashMap存储字符串并遍历
键:String
值:String
(2)HashMap存储自定义对象并遍历
键:String
值:Student
(3)HashMap存储自定义对象并遍历
键:Student(重写hashCode和equals方法,自动生成)
值:String
需求:如果对象的成员变量值都相同,我们则认为是同一个对象。
3:TreeMap
(1)TreeMap存储字符串并遍历
键:String
值:String
(2)TreeMap存储自定义对象并遍历
键:String
值:Student
(3)TreeMap(传入一个比较器comparator)存储自定义对象并遍历
键:Student
值:String
需求:如果对象的成员变量值都相同,我们则认为是同一个对象。
同时,我们还要按照年龄排序。
4:案例
(1)统计字符串中每个字符出现的次数。
//创建集合对象
TreeMap tm = newTreeMap();
String str = "cbxzbvavdvgd";
//将字符串转成字符数组
char[] chs = str.toCharArray();
//遍历字符数组
for (char ch : chs) {
//将拿到的字符去集合中找对应的值,根据返回值进行相应的操作
Integeri = tm.get(ch);
if(i == null) {
tm.put(ch, 1);
}else {
i++;
tm.put(ch, i);
}
}
//将得到的键值对集合,根据转变成字符串,并将结果按照要求的格式打印出来
//创建可变字符串对象
StringBuilder sb = new StringBuilder();
//得到集合中所有的键 并遍历
Set set = tm.keySet();
for(char ch:set)
{
//根据键找到对应的值,然后按照要求的格式拼接
Integeri = tm.get(ch);
sb.append(ch).append("(").append(i).append(")");
}
//将结果转成字符串,并输出
String result = sb.toString();
System.out.println(result);
(2)HashMap嵌套HashMap的使用。
//创建集合对象--czbk
HashMap>czbk = new HashMap>();
//创建并添加元素对象 yr和jy
HashMap yr = newHashMap();
yr.put("01","zhangsan");
yr.put("02", "lisi");
czbk.put("yr",yr );
HashMap jy = newHashMap();
jy.put("01","wangwu");
jy.put("02","zhaoliu");
czbk.put("jy", jy);
//遍历集合
//创建集合对象--czbk
HashMap>czbk = new HashMap>();
//创建并添加元素对象 yr和jy
HashMap yr = newHashMap();
yr.put("01","zhangsan");
yr.put("02", "lisi");
czbk.put("yr",yr );
HashMap jy = newHashMap();
jy.put("01","wangwu");
jy.put("02","zhaoliu");
czbk.put("jy", jy);
//遍历集合
//先拿到czbk集合中所有的键,并遍历
Set czbkKeys =czbk.keySet();
for(String czbkKey :czbkKeys)
{
//打印czbk集合中的键
System.out.println(czbkKey);
//在czbk集合中,根据键拿到对应的值,因为值也是一个键值对集合,所以我们在遍历一次
HashMaphm=czbk.get(czbkKey);
//拿到(czbk的值集合)中的所有的键
Setset = hm.keySet();
//遍历,根据键找到对应的值,并将结果输出
for(Strings :set)
{
String value = hm.get(s);
System.out.println("\t"+s+"***"+value);
}
}