黑马程序员-java集合类总结

时间:2023-02-19 15:25:58

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


一、集合类概述

1、为什么出现集合类

面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储。集合就是存储对象最常用的一种方式。

2、数组和集合类同是容器,两者有何区别

数组虽然也可以存储对象,但其长度是固定的,数组中可以存储基本数据类型;集合长度是可变的,集合只能存储对象。

3、集合类的特点

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

4、集合类的关系图

黑马程序员-java集合类总结

1)java集合的框架大致可分为两大类,一类是Collection,另一类是Map。Collection接口实现了Iterator接口,即迭代器接口,这意味着Collection接口的实现类可以调用Iterator接口的方法实现迭代器行为。Collection和Map的不同之处在于,Collection类集合存储单个对象,而Map类集合存储两个对象的键值对。

2)java提供了Collections、Arrays两个工具类,提供诸如集合排序、数组和集合的互换等功能。

3)Collection接口下又分为List接口与Set接口。List接口的主要实现类为ArrayList、LinkedList、Vector;Set接口的主要实现类为HashSet、TreeSet、LinkedHashSet。


二、Collection

1、常见操作

因为Collection为接口,不能建立对象,我们以ArrayList为例,

例1:

import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
//打印集合
System.out.println("原集合:"+al);
//判断元素
System.out.println("java03是否存在:"+al.contains("java03"));
System.out.println("集合是否为空:"+al.isEmpty());
//获取个数,即集合长度
System.out.println("Size="+al.size());
//删除元素
al.remove("java02");
System.out.println("删除后集合:"+al);
//清空集合
al.clear();
System.out.println("清空后集合:"+al);
}
}

输出结果:

原集合:[java01, java02, java03]
java03是否存在:true
集合是否为空:false
Size=3
删除后集合:[java01, java03]
清空后集合:[]

2、迭代器

什么是迭代器呢?其实就是集合取出元素的方式。

例2:

import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
al.add("AAA");
al.add("BBB");
al.add("CCC");
//获取迭代器
Iterator it=al.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
}
}

输出结果:

AAA
BBB
CCC


三、List

元素是有序的,元素可以重复。因为该集合体系有索引。

1、List特有方法:凡是可以操作角标的方法都是该体系特有的方法。

例3:

import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
//添加元素
al.add("java001");
al.add("java002");
al.add("java003");
al.add("java004");
al.add("java005");
//List特有方法
//在指定位置添加元素
al.add(1,"java");
//删除指定位置元素
al.remove(2);
//修改元素
al.set(2,"java007");
//通过角标获取元素
for (int x=0;x<al.size() ;x++ )
{
System.out.println("al("+x+")="+al.get(x));
}
}
}
输出结果:

al(0)=java001
al(1)=java
al(2)=java007
al(3)=java004
al(4)=java005

2、List特有的迭代器:ListIterator

在迭代时,不可以通过集合对象的方法操作集合中的元素,否则会发生ConcurrentModificationException(并发修改异常)。所以,在迭代时,只能用迭代器的方法操作元素。然而,Iterator提供的方法十分有限,只能对元素进行判断、取出及删除的操作。如果想要进行其他的操作,如添加、修改等,就需要使用其子接口:ListIterator。

该接口只能通过List集合的listIterator()方法获取。

例4:

import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
//添加元素
al.add("java001");
al.add("java002");
al.add("java003");
al.add("java004");
al.add("java005");
//并发修改异常
/*
Iterator it=al.iterator();
while(it.hasNext())
{
Object obj=it.next();
if(obj.equals("java002"))
al.add("java007"); //错误,并发修改异常
}
*/
//利用ListIterator在迭代过程中,添加\修改\删除元素
ListIterator lit=al.listIterator();
while (lit.hasNext())
{
Object obj=lit.next();
if(obj.equals("java002"))
lit.add("java008"); //正确,不会发生并发修改异常
System.out.println("obj="+obj);
}
System.out.println(al);
}
}

输出结果:

obj=java001
obj=java002
obj=java003
obj=java004
obj=java005
[java001, java002, java008, java003, java004, java005]


3、List集合各实现类的特点

Collection

|--List:元素是有序的,元素可以重复,因为该集合体系有索引。

|--ArrayList:底层使用数组数据结构。特点:查询速度很快,但是增删稍慢。线程不同步。

|--LinkedList:底层使用链表数据结构。特点:增删速度很快,查询稍慢。

|--Vector:底层使用数组数据结构。特点:线程同步,被ArrayList替代了。

1)ArrayList:

基于数组实现的,可以理解为可变数组,允许null元素,集合内部的元素顺序排列。

2)LinkedList:

基于双向链表实现的,允许null元素。通过提供特有的offerFirst()、peekFirst()、pollFirst()等方法,使LinkedList可用于模拟堆栈(stack)、队列(queue)或双向队列(deque)数据结构。

例5:使用LinkedList模拟一个队列数据结构。队列:先进先出

class DuiLie
{
private LinkedList ll;
DuiLie()
{
ll=new LinkedList();
}
public void add(Object obj)
{
ll.addFirst(obj);
}
public Object get()
{
return ll.removeLast();
}
public boolean isEmpty()
{
return ll.isEmpty();
}
public int size()
{
return ll.size();
}
public void sop()
{
for (int x=0;x<ll.size() ;x++ )
{
System.out.println(ll.get(x));
}
}
}

延伸阅读:请参阅When to use LinkedList<> over ArrayList<>?来了解,两种集合的使用场合。

3)Vector

Vector非常类似ArrayList,两者的主要区别在于,Vector是同步的,而ArrayList不同步。因此,在性能上Vector稍差于ArrayList。

4、List集合判断元素是否相同的依据

List集合根据元素的equals()来判断元素是否相同。contains()、remove()等方法在判断集合中的元素是否为目标元素时,也是调用equals()方法。

例6:

import java.util.*;
class Test
{
public static void main(String[] args)
{
//去除重复学生案例
ArrayList al=new ArrayList();
al.add(new Student("A",1));
al.add(new Student("A",1));
al.add(new Student("B",1));
al.add(new Student("B",1));
al.add(new Student("B",1));
al.add(new Student("B",2));
al.add(new Student("C",3));
al.add(new Student("C",3));
System.out.println("old:"+al);
al=SingleElement(al);
System.out.println("new:"+al);
}
//去除重复元素。此例证明,List集合通过equals()方法判断元素是否相同。
//即,contains(),remove()方法,底层调用的都是equals()方法;
public static ArrayList SingleElement(ArrayList al)
{
ArrayList newAl=new ArrayList();
Iterator it=al.iterator();
while (it.hasNext())
{
Object obj=it.next();
if(!newAl.contains(obj))
newAl.add(obj);
}
return newAl;
}
}
class Student
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写equals方法,学生的名字和年龄都相同时才认为两者相同
public boolean equals(Object obj)
{
if (!(obj instanceof Student))
return false;
Student st=(Student)obj;
return (this.name.equals(st.name))&&(this.age==st.age);
}
public String toString()
{
return "("+this.name+","+this.age+")";
}
}

输出结果:

old:[(A,1), (A,1), (B,1), (B,1), (B,1), (B,2), (C,3), (C,3)]
new:[(A,1), (B,1), (B,2), (C,3)]

四、Set

Set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复。

|--HashSet:底层使用哈希表数据结构。线程是非同步的

|--TreeSet:底层使用二叉树数据结构。可以对元素进行排序。

1、HashSet:

底层使用哈希表数据结构。线程是非同步的。

保证元素唯一性原理:判断元素的hashCode值是否相同,若相同,则继续通过equals方法判断元素是否相同。

两个元素相同,则它们的hashCode值一定相同,但若两个元素不相同,则它们的hashCode值不一定不相同。

例7:

import java.util.*;
class Test
{
public static void main(String[] args)
{
/*去除重复学生案例2
/*此例证明,HashSet集合通过hashCode()、equals()方法判断元素是否相同。
/*即,contains(),remove()方法,底层调用的都是这两个方法;
/******************************************************************/
HashSet hs=new HashSet();
hs.add(new Student("A",1));
hs.add(new Student("A",1));
hs.add(new Student("B",1));
hs.add(new Student("B",1));
hs.add(new Student("B",1));
hs.add(new Student("B",2));
hs.add(new Student("C",3));
hs.add(new Student("C",3));
System.out.println("Set:"+hs);
}
}
class Student
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写equals方法,学生的名字和年龄都相同时才认为两者相同
public boolean equals(Object obj)
{
if (!(obj instanceof Student))
return false;
Student st=(Student)obj;
return (this.name.equals(st.name))&&(this.age==st.age);
}
//复写hashCode方法
public int hashCode()
{
return name.hashCode()+age*37;
}
public String toString()
{
return "("+this.name+","+this.age+")";
}
}

输出结果:

Set:[(A,1), (B,1), (B,2), (C,3)]

2、TreeSet

底层使用二叉树数据结构。可以对元素进行排序。

保证元素唯一性的原理:compareTo()方法返回0,则认为两个元素相同。

TreeSet有两种排序方式:

1)元素自身具有比较性

方法:让元素所属的类继承Comparable接口,并重写compareTo()方法,则元素自身就具有了比较性。这种方式也称为元素的自然顺序。

例8:

import java.util.*;
class Test
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet();
ts.add(new Student("asd",20));
ts.add(new Student("aasdfd",10));
ts.add(new Student("gedd",22));
ts.add(new Student("re",23));
ts.add(new Student("2013d",12));
ts.add(new Student("x",9));
System.out.println("排序后"); //即添加元素时,集合自动排序
Iterator it=ts.iterator();
while (it.hasNext())
{
Student s=(Student)it.next();
System.out.println(s.getName()+"..."+s.getAge());
}
}
}
//TreeSet第一种排序法,让元素具有比较性
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写compareTo方法,以学生的年龄为主要条件,姓名为次要条件,升序排序
public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s=(Student)obj;
System.out.println(s.name+"..."+s.age);
int num=new Integer(this.age).compareTo(new Integer(s.age));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String toString()
{
return name+"@"+age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}

输出结果:

asd...20
asd...20
asd...20
asd...20
gedd...22
asd...20
aasdfd...10
asd...20
aasdfd...10
排序后
x...9
aasdfd...10
2013d...12
asd...20
gedd...22
re...23

由结果可知,1)TreeSet在添加元素时,会利用compareTo方法依次与集合中已有的元素进行比较;2)根据compareTo方法的返回值判断两个元素的先后顺序,从而完成排序。

2)让集合自身具有比较性

当元素自身不具有比较性,或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。

方法:自定义一个比较器类继承Comparator接口,重写compare()方法。

例9:

import java.util.*;
class Test
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet(new MyComparator());//构造TreeSet时,传入自定义比较器
ts.add(new Student("asd",20));
ts.add(new Student("aasdfd",10));
ts.add(new Student("gedd",22));
ts.add(new Student("re",23));
ts.add(new Student("2013d",12));
ts.add(new Student("x",9));
System.out.println("排序后"); //即添加元素时,集合自动排序
Iterator it=ts.iterator();
while (it.hasNext())
{
Student s=(Student)it.next();
System.out.println(s.getName()+"..."+s.getAge());
}
}
}
//TreeSet第一种排序法,让元素具有比较性
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写compareTo方法,以学生的年龄为主要条件,姓名为次要条件,升序排序
public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s=(Student)obj;
System.out.println(s.name+"..."+s.age);
int num=new Integer(this.age).compareTo(new Integer(s.age));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String toString()
{
return name+"@"+age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
//TreeSet第二种排序方法,让集合具有比较性
class MyComparator implements Comparator
{
//重写compare方法,以学生名字的长度为主要条件,学生的年龄为次要添加,升序排序
public int compare(Object o1,Object o2)
{
Student s1=(Student)o1;
Student s2=(Student)o2;
int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}

输出结果:

排序后
x...9
re...23
asd...20
gedd...22
2013d...12
aasdfd...10
由结果可知,TreeSet采用第二种排序方式时,是根据比较器中的compare()方法来对元素进行比较排序的。


五、Map

Map:该集合存储键值对,而且键值不可重复。

|--HashTable:底层是哈希表数据结构,不可以存入null键和null值。该集合是线程同步的,效率低。

|--HashMap:底层是哈希表数据结构,允许存入null键和null值。该集合是不同步的,效率高。

|--TreeMap:底层是二叉树数据结构。线程不同步。可以给集合中的键进行排序。

Map和Set很像,其实Set的底层就是使用Map实现的。

1、基本功能

例10:

import java.util.*;
class Test
{
public static void main(String[] args)
{
//基本功能
HashMap<String,String> hm=new HashMap<String,String>();
//添加元素。如果添加时,键相同,则该键对应的值会覆盖原有键对应的值,并返回原来的值
System.out.println(hm.put("01","A"));
System.out.println(hm.put("01","D"));
hm.put("02","B");
hm.put("03","C");
System.out.println(hm.containsKey("01"));//判断是否包含某键
System.out.println(hm.get("01")); //通过键查找对应的值
Collection<String> coll=hm.values(); //获得Map集合中所有的值
System.out.println(coll);
System.out.println(hm);
}
}

输出结果:

null
A
true
D
[D, B, C]
{01=D, 02=B, 03=C}
2、取出元素方式

Map集合提供两种取出方式:

1)Set<K> keySet():将Map中所有的键存入到一个Set集合。Set集合具备迭代器,可以通过迭代方式取出所有的键,然后再用get()方法获取每一个键对应的值。

2)Set<Map.Entry<K,V>> entrySet():将Map中的所有映射关系存入到一个Set集合中,而这个映射关系的数据类型就是:Map.Entry
例11:

import java.util.*;
class Test
{
public static void main(String[] args)
{
//学生案例
TreeMap<Student,String> hm=new TreeMap<Student,String>(new StuNameComparator());
hm.put(new Student("AAA",20),"BeiJing");
hm.put(new Student("B",22),"HangZhou");
hm.put(new Student("CC",23),"ChengDu");
//第一种取出方式
Set<Student> ks=hm.keySet(); //先获取Map集合的所有键的Set集合:keySet()
Iterator<Student> it=ks.iterator(); //获取迭代器
while (it.hasNext())
{
Student s=it.next();
String add=hm.get(s); //有了键,通过Map集合的get()方法获取其对应的值
System.out.println(s+"..."+add);
}
//第二种取出方式
Set<Map.Entry<Student,String>> es=hm.entrySet(); //将Map集合中的映射关系存入到Set集合中,entrySet()
Iterator<Map.Entry<Student,String>> it2=es.iterator();
while (it2.hasNext())
{
Map.Entry<Student,String> me=it2.next();
Student s=me.getKey(); //Map.Entry特有的获取键的方法
String str=me.getValue(); //Map.Entry特有的获取值的方法
System.out.println(s+"///"+str);
}
}
}
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s=(Student)obj;
return this.name.equals(s.name)&&this.age==s.age;
}
public int hashCode()
{
return name.hashCode()+10*age;
}
public int compareTo(Student s)
{
int num=this.name.compareTo(s.name);
if(num==0)
return new Integer(this.age).compareTo(new Integer(s.age));
return num;
}
public String toString()
{
return name+"@"+age;
}
}
class StuNameComparator implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{
int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}

输出结果:

B@22...HangZhou
CC@23...ChengDu
AAA@20...BeiJing
B@22///HangZhou
CC@23///ChengDu
AAA@20///BeiJing

六、Collections与Arrays工具类

1、Collections

对Collection类集合进行操作的工具类。

常见方法:

static <T extends Comparable<? super T>> void sort(List<T> list) //对List进行排序

static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) //根据比较器的排序,返回集合中的最大元素

static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) //以二分法的形式,在List集合中查找某元素

static <T> void fill(List<? super T> list, T obj) //将集合中的元素全部替换成指定元素

例12:

import java.util.*;
class CollectionsTest
{
public static void main(String[] args)
{
ArrayList<String> list=new ArrayList<String>();
list.add("fcg");
list.add("defd");
list.add("aa");
list.add("aaa");
list.add("d");
//sort()
System.out.println("排序前:"+list);
Collections.sort(list,new StrLenComparator());
System.out.println("排序后:"+list);
//反转
Collections.reverse(list);
System.out.println("reverse后:"+list);
//max()
System.out.println("默认max:"+Collections.max(list));
System.out.println("根据比较器的max:"+Collections.max(list,new StrLenComparator()));
//binarySearch()
System.out.println("aaa在第"+Collections.binarySearch(list,"aaa")+"个");
//fill(),全部替换
Collections.fill(list,"aa");
System.out.println("fill后:"+list);
//replaceAll
Collections.replaceAll(list,"aa","bb");
System.out.println("replaceAll后:"+list);
}
}
class StrLenComparator implements Comparator<String>
{
public int compare(String str1,String str2)
{
int num=new Integer(str1.length()).compareTo(new Integer(str2.length()));
if(num==0)
return str1.compareTo(str2);
return num;
}
}

输出结果:

排序前:[fcg, defd, aa, aaa, d]
排序后:[d, aa, aaa, fcg, defd]
reverse后:[defd, fcg, aaa, aa, d]
默认max:fcg
根据比较器的max:defd
aaa在第2个
fill后:[aa, aa, aa, aa, aa]
replaceAll后:[bb, bb, bb, bb, bb]


延伸阅读:

关于 Java Collections API 您不知道的 5 件事,第 1 部分关于 Java Collections API 您不知道的 5 件事,第 2 部分

2、Arrays

用于操作数组的工具类。

我们只介绍其中一个:

数组变集合:

static <T> List<T> asList(T... a) //将数组转成集合
将数组转成List集合后,可以使用集合的思想和方法操作数组中的元素,但不可以使用集合的增删方法。因为数组的长度是固定的。


集合变数组:

Collection中的方法:

public <T> T[] toArray(T[] a) //将集合转成数组
将集合转成数组后,就限定了对元素的操作,不能再进行增删操作了。
例13:

import java.util.*;
class CollectionsTest
{
public static void main(String[] args)
{
//数组变集合
String[] arr={"a","bb","ccc"};
List<String> list=Arrays.asList(arr);
//list.add("dddd");//错误,不能使用集合的增删功能
ArrayList<String> newList=new ArrayList<String>(Arrays.asList(arr));
newList.add("dddd");//OK!
System.out.println(list);
//数组变集合2
int[] num={1,2,3};
System.out.println(Arrays.asList(num));//数组元素必须为对象,否则只将该数组作为集合中的元素存在
Integer[] num2={1,2,3};
System.out.println(Arrays.asList(num2));//这样就没问题了
//集合变数组
String[] arr1=list.toArray(new String[list.size()]);
for (String str:arr1)
{
System.out.print(str+" ");
}
}
}

输出结果:

[a, bb, ccc]
[[I@188edd79]
[1, 2, 3]
a bb ccc

延伸阅读:

欲深入了解ArrayList、HashMap、HashSet等的实现原理,请参考

深入Java集合学习系列:ArrayList的实现原理等系列。


---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com