------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1、概念:集合类是一个容器,只用于存储对象。
2、与数组相比,集合用于存储对象,且长度可变。数组用于存储基本数据类型,长度不可变。
3、为什么出现集合类,java语言的编程思想是面向对象,对象就会很多,集合就是为了更好的管理对象。
4、集合框架:
List:元素是有序的,元素可以重复
ArrayList:底层为数组结构,带有脚标。特点:查询快,增删慢,线程不同步。
LinkedList:底层为链表数据结构,没有脚标。特点:查询慢,增删快,线程不同步。
Vector:底层为数组结构,带有脚标。特点:查询、增删都慢,线程同步,因为效率低,jdk升级后被ArrayList代替。
Set:元素是无序的,元素不可以重复。
Collection
接口中定义了集合框架中共性的功能:
添加:
add(E):添加一个元素
addAll(Conllection):添加一个集合中所有元素
删除:
remove(Object):移除一个元素
removeAll(Conllection):移除本集合中与传入集合相同的元素
clear():清空集合
判断:
contains(Object):是否包含某个元素
containsAll(Conllection):是否包含一个集合中的所有元素
isEmpty():判断集合是否为空
获取:
iterator():迭代器
size():获取集合的长度
取交集:
retainAll(Conllection):保留本集合与传入集合相同的元素
转成数组:
toArray()
List接口中的特有方法:
添加:
add(int index, E element):根据脚标添加
add(int index,Conllection)
删除
remove(int index):根据脚标移除元素
修改:
set(int index,E):替换指定脚标的元素
查询:
get(int index):获取指定脚标的元素
listIterator():特有迭代器,在迭代过程中可以对数据进行添加、修改操作
indexOf(Object):获取元素的脚标
subList(int fromIndex, int toIndex):获取集合中某一段
LinkedList中特有的方法:
添加:
addFirst()
addLast()
获取:
getFirst()
getLast()
删除:
removeFirst()
removeLast()
上述两个方法同样可以获取元素,但是跟获取中两个方法比较,此方法会删除元素
在jdk1.6版本后
添加:
offerFirst()
offerLast()
获取:
peekFirst()
peekLast()
删除:
pollFirst()
pollLast()
Vector中特有方法:
枚举:相当于迭代器
代码示例:
import java.util.*;
class VectorDemo
{
public static void main(String[] args)
{
Vector v = new Vector();
v.add("person01");
v.add("person02");
v.add("person03");
v.add("person04");
//Vector枚举方法相当于迭代器
Enumeration e = v.elements();
while (e.hasMoreElements())
{
sop(e.nextElement());
}
}
public static void sop(Object o)
{
System.out.println(o);
}
}
练习1:模拟队列结构和堆栈结构
队列结构:先进先出,就像水管
堆栈结构:先进后出,就像水杯
import java.util.*;
class DuiLie
{
private LinkedList link;
DuiLie()
{
link = new LinkedList();
}
public void myAdd(Object o)
{
link.addFirst(o);
}
public Object myGet()
{
//return link.removeLast();
return link.removeFirst();//要改为堆栈结构,只需修改取数据的动作,同时
//也可以修改存储的动作
}
public boolean isNull()
{
return link.isEmpty();
}
}
练习2:去除ArrayList中的重复元素
public static ArrayList removeSame(ArrayList al)
{
/*
定义一个新的容器,将要去除重复元素的容器进行迭代,
新的容器对迭代出来的元素进行是否包含判断,如果包含
就存储进新的容器
*/
ArrayList newAl = new ArrayList();
Iterator it = al.iterator();
while (it.hasNext())
{
Object o = it.next();
if (!newAl.contains(o))
{
newAl.add(o);
}
}
return newAl;
}
当要存储自定义对象时,在自定义对象类中呀重写Object类中的equals方法,因为集合的contains方法会调用对象的equals方法
比如要在集合中存储人对象,当人的姓名和年龄相同时,就视为同一个人,那么,人这个类中就可以这样描述
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
//复写Object类中的equals方法
public boolean equals(Object o)
{
if (!(o instanceof Person))
return false;
Person p = (Person)o;
return this.name.equals(p.name) && this.age == p.age;
}
}
HashSet:底层是哈希表结构,元素是无序的,在存储元素的时候,先判断元素的哈希值,再判断元素是否相同
练习3:往HashSet存储自定义对象的时候,要复写Object类中的hashCode和equals方法:
那么在定义对象的时候就可以这样定义
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()
{
//System.out.println(this.name+"----hashCode......");
/*
return 60;
直接返回数值也可以,但是为了减少判断对象是否相同,也就是减少
equals方法,要自定义返回值,而且要不一样
*/
return this.name.hashCode()+age*39;//乘39是为了避免返回的哈希值一样
}
//复写Object类中的equals方法
public boolean equals(Object o)
{
if (!(o instanceof Person))
return false;
Person p = (Person)o;
//System.out.println(this.name+"----equals....."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
}
TreeSet:底层为二叉树结构
以34位基准,比34小的往左边放,比34大的往右边放,,在插入过程中,会找一个中间数作为比较,永远都遵循小的在左边
大的在右边
用TreeSet存储自定义对象时,在运行时会发生java.lang.ClassCastException(类型转换异常)
解决办法:
第一:可以让自定义对象具有比较性,实现Comparable接口,覆盖compareTo方法,
在compareTo方法里面自定义比较内容
第二:可以自定义一个比较器,实现comparator接口,覆盖compare方法,里面自定义比较内容,
将比较器作为参数传给TreeSet对象
注意:当两者都存在时,以比较器为主
代码示例:当用TreeSet集合存储自定义人对象时,按年龄升序排序,但是遇到姓名不同,年龄相同时,不能存储,所以当年龄相同时,要比较姓名
第一:让对象具有比较性
class Person implements Comparable//让对象具有比较性
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int compareTo(Object o)
{
if (!(o instanceof Person))
{
throw new RuntimeException("参数不匹配");
}
Person p = (Person)o;
/*
if (this.age>p.age)
{
return 1;
}
if (this.age==p.age)
{
return this.name.compareTo(p.name);
}
return -1;
*/
//同样是先比较年龄,主要条件,再比较姓名这个次要条件
int a = new Integer(this.age).compareTo(new Integer(p.age));//Integer自身具有默认的比较方法
if (a==0)
{
return this.name.compareTo(p.name);
}
return a;
}
}
第二:自定义比较器方法:
class MyComparator implements Comparator//定义比较器,实现Comparator接口
{
public int compare(Object o1,Object o2)
{
Person p1 = (Person)o1;
Person p2 = (Person)o2;
//比较主要条件,当年龄相同时,再比较次要条件,姓名
int a = new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
if (a==0)
{
//次要条件
return p1.getName().compareTo(p2.getName());
}
return a;
}
}
练习1:按字符串的长度进行排序
/*
自定义比较器,用于比较字符串长度,但是当用户存储的字符串长度一样,但是内容不一样时,
没有存储进去,所以当长度一样时,要进行字符串的比较
*/
class MyComparator implements Comparator
{
public int compare(Object o1,Object o2)
{
String s1 = (String)o1;
String s2 = (String)o2;
int a = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if (a==0)
{
return s1.compareTo(s2);
}
return a;
}
}
练习2:"90 -7 0 18 2 45 4" 将字符串中的数值进行排序。使用TreeSet完成
/*
分析:将字符串进行切割,将切割后的字符转成Integer存储进
TreeSet集合,TreeSet本身具备比较功能
*/
import java.util.*;
class TreeSetTest2
{
public static void main(String[] args)
{
String s= "90 -7 0 18 2 45 4";
String[] str = s.split(" ");//按空格对字符串进行切割
TreeSet t = new TreeSet();
for (int x=0; x<str.length; x++)
{
t.add(Integer.parseInt(str[x]));//转成Integer依次存储进TreeSet集合中
}
System.out.println(t);//打印集合
}
}
5、泛型:是一个安全机制,提高代码安全性
在编写代码时用<>指定类型,在编译时,如果传入不是指定类型,就会编译失败
避免强转
泛型可以定义在类上
//泛型定义在类上,在整个类中有效
class Test<T>
{
public void show(T t)
{
System.out.println("show----"+t);
}
//将泛型定义在接口上,不受类上泛型的影响
public <W> void print(W w)
{
System.out.println("print----"+w);
}
//静态方法不能直接访问类上的泛型
//如果静态方法操作的数据类型不确定,可以将泛型定义在方法上
/*
public static void print_2(T t)
{
System.out.println("show----"+t);
}
*/
}
泛型定义在接口上:
//泛型定义在接口中
interface Inter<E>
{
public void show(E e);
}
//在实现接口时,指定类型,在使用的时候,只能操作指定的类型
class InterImpl implements Inter<String>
{
public void show(String s)
{
System.out.println("show---"+s);
}
}
//如果操作的类型不确定,也可以不指定类型,在类中继续使用泛型
class InterImpl_2<E> implements Inter<E>
{
public void show(E e)
{
System.out.println("show---"+e);
}
}
泛型限定:
?通配符或者占位符
? extends E 可以接收E类型或者E的子类型 向上限定
? super E 可以接收E类型或者E的父类型 向下限定
代码示例:
class GenericTest4
{
public static void main(String[] args)
{
TreeSet<Student> t = new TreeSet<Student>(new MyComparator());
t.add(new Student("Student01",21));
t.add(new Student("Student02",19));
t.add(new Student("Student03",25));
t.add(new Student("Student04",22));
t.add(new Student("Student05",19));
printColl(t);
}
//定义一个打印集合的方法,限定泛型为Person或者Person的子类
public static void printColl(Collection<? extends Person> al)
{
Iterator<? extends Person> it = al.iterator();
while (it.hasNext())
{
Person p = it.next();
System.out.println("name:"+p.getName()+"-----age:"+p.getAge());
}
}
}
//定义一个比较器接收Person或者Person的子类
class MyComparator implements Comparator<Person>
{
public int compare(Person p1,Person p2)
{
int a = new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
if (a==0)
{
return p1.getName().compareTo(p2.getName());
}
return a;
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class Student extends Person
{
Student(String name,int age)
{
super(name,age);
}
}
class Worker extends Person
{
Worker(String name,int age)
{
super(name,age);
}
}
Map集合:接口 Map<K,V> 该集合存储的是键值对,一个key对应一个value
|--Hashtable:底层是哈希表结构,key和value都不能为null,线程是同步的,效率低
|--HashMap:底层是哈希表结构,key和value可以为null,线程是不同步的,效率高
|--TreeMap:底层是二叉树结构,key和value不可以为空,线程是不同步的,可以给key进行排序
添加:
put(K key, V value)
putAll(Map<? extends K,? extends V> m)
putAll(Map<? extends K,? extends V> m)
删除:
remove(Object key):在移除元素的时候,可以获取value的值
clear():清空
判断:
contains(Object key)
contains(Object value)
isEmpty()
获取:
get(Object key)
size()
values()
ketSet:
将map中的key存储到Set集合中,通过迭代器,根据key的用get(Object key)方法获取值
entrySet:
将map集合中的映射关系存储到Set集合中,这种映射关系的类型是Map.Entry
entrySet是map中一个静态内部接口,因为先存在键值对,才有映射关系,并且要直接访问map的映射关系,所以定义在内部
练习1、往HashMap中存储学生对象和地址,学生类中有姓名和年龄,姓名和年龄一样视为同一个学生,保证学生唯一性
class MapTest
{
public static void main(String[] args)
{
Map<Student,String> m = new HashMap<Student,String>();
Student s1 = new Student("张三",18);
Student s2 = new Student("李四",22);
Student s3 = new Student("王五",25);
Student s4 = new Student("赵六",20);
Student s5 = new Student("赵六",20);
m.put(s1,"广东省");
m.put(s2,"湖南省");
m.put(s3,"福建省");
m.put(s4,"黑龙江省");
m.put(s5,"北京市");
//keySet方式取出数据
Set<Student> keySet = m.keySet();
Iterator<Student> it = keySet.iterator();
while (it.hasNext())
{
Student s = it.next();
System.out.println("姓名:"+s.getName()+"-年龄:"+s.getAge()+"-地址:"+m.get(s));
}
//entrySet方式取出数据
Set<Map.Entry<Student,String>> entrySet = m.entrySet();
Iterator<Map.Entry<Student,String>> it2 = entrySet.iterator();
while (it2.hasNext())
{
Map.Entry<Student,String> me = it2.next();
Student s = me.getKey();
String address = me.getValue();
System.out.println("姓名:"+s.getName()+"-年龄:"+s.getAge()+"-地址:"+address);
}
}
}
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
//因为HashMap底层为哈希表结构,要保证唯一性,
//就要复写Object类中的hashCode方法
public int hashCode()
{
return name.hashCode()+age*39;
}
//并且要覆盖equals方法
public boolean equals(Object obj)
{
if (!(obj instanceof Student))
{
throw new ClassCastException("类型不匹配");
}
Student s = (Student)obj;
return this.name.equals(s.name) && this.age==s.age;
}
//因为可能需要往TreeSet集合里面存储学生
//所以实现Comparable接口覆盖compareTo方法
public int compareTo(Student s)
{
int a = new Integer(this.age).compareTo(new Integer(s.age));
if (a==0)
{
return this.name.compareTo(s.name);
}
return a;
}
}
练习2:获取一段字符串中每个字母出现的次数,并以a(次数)b(次数)这样的形式打印
/*
分析:
先将字符串转换成字符数组
将字符作为键,次数作为值存入TreeSet集合中,因为TreeSet集合自身具有排序功能
遍历字符数组,将每一个元素作为键去查找集合中是否有值
如果没值,就将(字符,1)存入集合,如果有值,就将(字符,原来的次数加1)存入集合
因为在map集合中,如果key一样,新的value会覆盖旧的value
*/
class MapTest2
{
public static void main(String[] args)
{
String s = "abcabce2fgh}oll.eabb";
s = getCharCount(s);
System.out.println(s);
}
//定义一个获取出现次数的方法
public static String getCharCount(String s)
{
char[] arr = s.toCharArray();//将字符串转换成字符数组
TreeMap<Character,Integer> hm = new TreeMap<Character,Integer>();
int count = 0;//定义计数器,记录次数
for (int x=0; x<arr.length; x++)
{
//判断数组中的元素是否为字母
if(!(arr[x]>'a' && arr[x]<'z' || arr[x]>'A' && arr[x]<'Z'))
continue;
Integer value = hm.get(arr[x]);//根据字符获取value
if(value!=null)
count = value;
count ++;
hm.put(arr[x],count);
count = 0;//要循环获取每个字符,计数器要清零
}
Set<Map.Entry<Character,Integer>> entrySet = hm.entrySet();
Iterator<Map.Entry<Character,Integer>> it = entrySet.iterator();
StringBuilder sb = new StringBuilder();//定义字符缓冲区对象
while (it.hasNext())
{
Map.Entry<Character,Integer> me = it.next();
Character ch = me.getKey();
Integer i = me.getValue();
sb.append(ch+"("+i+")");//将迭代出来的字符按照如下形式依次存入
}
return sb.toString();//将字符缓冲区转换为字符串,并返回
}
}
Map集合拓展:Map集合中嵌套集合
代码示例:
class MapTest3
{
/*
集合中嵌套集合
一个公司中有多个部门,一个部门有多个员工
将部门中的员工存储在List集合中,然后再将部门名称作为key,员工作为value
存储Map集合中
*/
public static void main(String[] args)
{
List<Employee> personnel = new ArrayList<Employee>();//人事部
List<Employee> technology = new ArrayList<Employee>();//技术部
HashMap<String,List<Employee>> compary = new HashMap<String,List<Employee>>();//公司
compary.put("人事部",personnel);
compary.put("技术部",technology);
personnel.add(new Employee(01,"zhangsan"));
personnel.add(new Employee(02,"lisi"));
personnel.add(new Employee(03,"wangwu"));
technology.add(new Employee(01,"张三"));
technology.add(new Employee(02,"王五"));
technology.add(new Employee(03,"李四"));
//通过entrySet方法取出元素
Set<Map.Entry<String,List<Employee>>> entrySet = compary.entrySet();
Iterator<Map.Entry<String,List<Employee>>> it = entrySet.iterator();
while (it.hasNext())
{
Map.Entry<String,List<Employee>> me = it.next();
String department = me.getKey();
List<Employee> al = me.getValue();
System.out.println(department);
printList(al);
}
}
public static void printList(List<Employee> l)//打印list集合中的元素
{
Iterator<Employee> it = l.iterator();
while (it.hasNext())
{
Employee e = it.next();
System.out.println("id:"+e.getId()+"----name:"+e.getName());
}
}
}
class Employee//员工类
{
private int id;
private String name;
Employee(int id,String name)
{
this.id = id;
this.name = name;
}
public String getName()
{
return name;
}
public int getId()
{
return id;
}
}
Collections:集合框架工具类,定义都是静态方法
Collection和Collections的区别?
Collection是集合框架中一个顶层接口,定义了集合中共性的方法。常用子类口有List和Set
Collections是集合框架工具类,提供了各种操作集合的静态方法,比如二分法查找、排序、同步方法
常用方法:
sort(List<T> list):对list集合进行自然排序
sort(List<T> list, Comparator<? super T> c):用自定义比较器进行排序
static void swap(List<?> list, int i, int j):交换元素
reverse(List<?> list):反转指定list集合中的元素
replaceAll(List<T> list,T oldVal,T newVal):替换list集合中某个值
reverseOrder():逆转现有比较器的排序方式
reverseOrder(Comparator<T> com):逆转自定义比较器的排序方式
shuffle(List<?> list):对list集合中的元素随机产生不同的顺序
注意:Collection中toArray方法:当需要限定对集合中的元素进行操作时,就把集合转成数组
定义转换后数组长度时,如果定义小于原集合的长度,该方法内部会新建一个数组,长度为原集合的长度
如果定义大于原集合长度,则不会新建一个数组,直接使用传递进来的集合,但是多余的部分将会是null值
Arrays:用于操作数组的工具类,定义的都是静态方法
asList:将数组变为list集合
注意:如果原数组存储的是对象,转换成集合时,数组中的元素就是集合中的元素
如果原数组存储的是基本数据类型,转换成集合时,那么会将数组对象作为集合中的元素。
将数组转换成集合后,不可以使用集合中增删方法,因为数组的长度是固定的。
可变参数:一定要将可变参数定义在参数列表的最后
代码示例:
class ParamMethodDemo
{
public static void main(String[] args)
{
show("haha",2,3,4,5,6);
}
public static void show(String str,int... arr)
{
System.out.println(arr.length);
}
}
静态导入:在导入包时,加上static关键字,导入的是类中静态方法,在调用时,可以省略类名.
注意:当类重名时,要指定包名。
当方法重名时,要指定类名或者对象。