------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
集合类概述
数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
Collection: 集合的顶层接口
方法:
添加功能:
boolean add(E e) 把给定的数据 添加到集合中
boolean addAll(Collection c) 把给定的集合的元素,添加到当前集合中
删除功能:
void clear() : 将集合中的元素清空
boolean remove(Object o): 将给定的元素在集合中删除
boolean removeAll(Collection c)将给定的集合元素,在当前集合中删除
只要在当前集合中,删除了给定的集合元素(只要有一个被删除),则返回true
否则返回false
长度功能:
int size() 返回集合中元素的个数
转换功能:
Object[] toArray(): 把集合 转换成数组
判断功能:
boolean contains(Object o)判断当前集合中是否包含给定的元素
boolean isEmpty() 判断当前集合中的元素 是否为空
boolean containsAll(Collection<?> c) 判断当前集合中,是否包含给定集合中所有的元素(只要有一个未包含的就返回false)
遍历功能(迭代器):
Iterator<E> iterator(): 遍历集合中的元素
交集功能:
boolean retainAll(Collection<?> c)判断两个集合中相同的元素
例题:c1.retainAll(c2);
把两个集合中相同的元素,存储到当前集合c1中,c2集合中的元素步伐上改变
当c1集合中的元素发生变化,则返回true
当c2集合中的元素无变化,则返回false
Collection案例练习:
1、Collection集合存储字符串并遍历
public class CollectionTest {
public static void main(String[] args) {
// 创建集合对象
Collection c = new ArrayList();
//添加元素到集合
c.add("赵学友");
c.add("爱凤姐");
//遍历??
//获取迭代器对象,进行遍历
Iterator it = c.iterator();
//判断是否有下一个元素
while (it.hasNext()){
//获取下一个元素
System.out.println(it.next());
}
}
}
2、使用Collection集合 存储自定义对象,并遍历(迭代器)
//自定义Person类
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.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 String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class CollectionTest2 {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();
//创建元素对象
Person p1 = new Person("天花", 10);
Person p2 = new Person("菊花", 11);
Person p3 = new Person("桃花", 12);
//添加元素到集合
c.add(p1);
c.add(p2);
c.add(p3);
//遍历集合
//获取到迭代器对象,进行遍历
Iterator it = c.iterator();
//判断是否有下一个元素
while(it.hasNext()){
//获取下一个元素
Person p = (Person)it.next();
System.out.println(p.getName() + "--" + p.getAge());
}
}
}
List:
特点:
它是Collection子集合
可以存储重复的元素
有序(元素存与取顺序一致)
List:
特有方法:
void add(int index, E element) : 在当前集合中指定位置 添加给定的元素
Object get(int index) 返回集合中指定位置的元素。
int indexOf(Object o)返回此集合中第一次出现的指定元素的索引;如果此集合不包含该元素,则返回 -1。
E remove(int index)移除集合中指定位置的元素
E set(int index, E element)用指定元素替换集合中指定位置的元素
ListIterator<E> listIterator() 返回此集合元素的列表迭代器
并发修改异常产生的原因及解决方案:
ConcurrentModificationException 并发修改异常:
在使用迭代器遍历的过程中,原有集合中的元素个数不能发生改变,否则抛出 并发修改异常
解决方式:
普通for:
添加的新元素到原有集合的末尾添加
ListIterator:
添加的新元素在Java元素的后面添加
1、请使用list集合 存储字符串对象, 并遍历
public class ListDemo2 {
public static void main(String[] args) {
//创建List集合对象
List list = new ArrayList();
//把字符串元素 添加到集合
list.add("天花");
list.add("麻花");
list.add("棉花");
//遍历 迭代器方式
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("-----------------");
for ( Iterator it2 = list.iterator() ; it2.hasNext() ; ) {
System.out.println(it2.next());
}
System.out.println("-----------------");
//普通for循环遍历
for (int i = 0; i < list.size(); i++) {
//每一个集合中的元素
Object obj = list.get(i);
System.out.println(obj);
}
}
}
2、需求: 通过List集合存储自定义对象,并遍历
public class ListDemo3 {
public static void main(String[] args) {
// 创建集合对象
List list = new ArrayList();
// 创建元素对象
Person p1 = new Person("范冰冰", 28);
Person p2 = new Person("李冰冰", 30);
Person p3 = new Person("赵日天", 29);
// 添加元素到集合
list.add(p1);
list.add(p2);
list.add(p3);
// 遍历
for (int i = 0; i<list.size(); i++) {
//获取集合中的元素
//Object obj = list.get(i);
Person p = (Person)list.get(i);
System.out.println(p.getName() +"--" + p.getAge());
}
//迭代器方式
for (Iterator it = list.iterator(); it.hasNext(); ) {
//获取元素
Person p = (Person)it.next();
System.out.println(p.getName() +"--" + p.getAge());
}
}
}
List的三个子类的特点:
ArrayList:
底层: 数组结构, 增删慢 ,查询快
线程不同步,效率高,不安全
Vector:
底层: 数组结构, 增删慢,查询快
线程同步,效率低, 安全
LinkedList:
底层: 链表结构, 增删快,查询慢
线程不同步,效率高,不安全
Vector:
特有方法:
public void addElement(Object obj) 添加元素到集合 ———— add(Obejct obj)
public Object elementAt(int index) 返回集合中给定位置上的元素 ---- get(int index)
public Enumeration elements()返回此向量的组件的枚举 --- iterator()
boolean hasMoreElements() ---- hasNext()
Object nextElement() --- next();
LinkedList:
特有方法:
public void addFirst(Object e)将指定元素插入此列表的开头
public void addLast(Object e)将指定元素添加到此列表的结尾。
public Object getFirst()返回此列表的第一个元素
public Object getLast()返回此列表的最后一个元素。
public Object removeFirst()移除并返回此列表的第一个元素。
public Object removeLast()移除并返回此列表的最后一个元素。
例题:请用LinkedList模拟栈数据结构的集合,并测试
//teacher类
public class Teacher {
private String name;
private int age;
public Teacher() {
super();
}
public Teacher(String name, int age) {
super();
this.name = name;
this.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 boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Teacher other = (Teacher) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String toString() {
return "Teacher [name=" + name + ", age=" + age + "]";
}
}
//我的栈 集合
public class MyStack {
//用LinkedList模拟栈数据结构的集合
private LinkedList list = new LinkedList();
//入栈
public void push(Object obj){
list.addFirst(obj);
}
//弹栈
public Object pop(){
return list.removeFirst();
}
//判断栈是否为空
public boolean isEmpty(){
return list.size() == 0;
}
}
//栈 集合测试
public class MyStackTest {
public static void main(String[] args) {
//创建栈集合对象
MyStack stack = new MyStack();
//添加元素
stack.push("A");
stack.push("B");
stack.push("C");
//栈不为空,弹栈
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
泛型:
泛型: Generic
泛型的格式:
< 泛型类型 >
注意: 这里的泛型类型可以任意的内容,基本的数据类型,
比如 String Person QQ、T、E、K
泛型常见在哪里可以使用?
接口上使用泛型
类上使用泛型
方法上使用泛型
泛型类:
把泛型定义在类上
格式:public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型
<QQ> 定义一个泛型
QQ 使用当前这个泛型,这个QQ在程序运行的时候,会对应着一个具体的数据类型
例如:
public class Test<QQ> {
private QQ name;
public void setName(QQ name){
this.name = name;
}
public QQ getName(){
return name;
}
}
泛型方法
把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
K 的类型是在创建对象的时候 确定的
TT 类型是在调用方法的时候 确定的
这里的K,TT 理解为 是一种数据类型的变量名
注意:创建多个泛型类对象的时候,可以赋值不同的泛型类型,不会产生冲突
例如:
public class GenericMethod<K> {
//普通方法
public String method(String str){
return str + "哈哈";
}
//泛型方法
public K function(K str){
return str;
}
//泛型方法
public <TT> TT show(TT str) {
return str;
}
}
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>
例如
public interface Inter<TT> {
public TT show(TT str);
}
实现泛型接口的类:
方式1:在编写类的时候实现泛型接口
public class InterImpl implements Inter<String> {
@Override
public String show(String str) {
return str;
}
}
方式2:创建对象的时候,确定泛型的数据类型
InterImpl<QQ> 声明泛型QQ
implements Inter<QQ> 使用QQ 所代表的数据类型
public class InterImpl<QQ> implements Inter<QQ>{
@Override
public QQ show(QQ str) {
return str;
}
}
泛型高级之通配符
泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类了
? extends E
向上限定,E及其子类
?代表了 可以是E所对应的数据类型,或者是E的子类类型
例如:
? extends Animal
? 代表了 Animal类型,或者Animal子类类型
? super E
向下限定,E及其父类
?代表了 可以使 E所对应的数据类型,或者是E的父类类型
例如:
? super Dog
? 代表的是 Dog类型,或者是Dog的父类类型
增强for的概述和使用:
增强for概述 jdk1.5出现的新特性
简化数组和Collection集合的遍历
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
好处:简化遍历
注意事项:增强for的目标要判断是否为null
把前面的集合代码的遍历用增强for改进
注意:
如果需要使用索引, 请使用传统for
如果不需要使用索引,推荐是使用增强for(), 也可以使用迭代器
集合案例:
1、去除ArrayList中重复字符串元素方式1-新集合方式
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Java");
list.add("HTML");
list.add("CSS");
list.add("CSS");
list.add("CSS");
list.add("JavaScript");
//a: 创建新的集合
ArrayList newList = new ArrayList();
//b: 遍历原有集合,得到每一个元素
for (int i=0; i<list.size(); i++) {
//得到每一给元素
Object obj = list.get(i);
//c: 判断当前元素在新集合是否存在
if (newList.contains(obj)) {
//已经存在,不存
} else {
//没有存在, 存储进来
newList.add(obj);
}
}
System.out.println("newList:"+newList);
}
}
2、去除ArrayList中重复字符串元素方式2-快速排序算法
public class ArrayListDemo2 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Java");
list.add("HTML");
list.add("CSS");
list.add("CSS");
list.add("CSS");
list.add("JavaScript");
//控制比较的次数
for (int i = 0; i < list.size()-1; i++) {
//控制比较的元素
for (int j = i+1; j < list.size(); j++) {
//list.get(i) 和 list.get(j)
//判断当前两个元素的值,是否相同,如果相同,删除后面的元素
if (list.get(i).equals(list.get(j))) {
//删除后面元素
list.remove(j);
j--;
}
}
}
//显示结果
System.out.println(list);
}
}
3、ArrayList存储自定义对象并遍历增强for版
public class ForEachTest2 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<Person>();
list.add(new Person("赵日天"));
list.add(new Person("啦啦"));
list.add(new Person("哈哈"));
for(Person p : list) {
System.out.println(p.getName());
}
}
}
静态导入:
静态导入概述
格式:import static 包名….类名.方法名;
可以直接导入到方法的级别
注意事项
方法必须是静态的
可变参数
变参数概述
定义方法的时候不知道该定义多少个参数
格式
修饰符 返回值类型 方法名(数据类型… 变量名){}
注意:
这里的变量其实是一个数组
如果一个方法有可变参数,并且有多个参数,
那么,可变参数肯定是最后一个,并且一个方法中只能有一个可变参数
Set集合
Set:一个不包含重复元素的集合
HashSet:
底层: 哈希表结构
特点:
不包含重复元素
无序(元素的存与取的顺序不一致)
线程不同步--不安全--效率高
HashSet如何保证元素唯一性:
重写 hashCode()方法 与 equals()方法
LinkedHashSet:
底层:哈希表结构 + 链表结构
特点:
不包含重复元素
由链表保证元素有序
由哈希表保证元素唯一
线程不同步--不安全--效率高
TreeSet:
底层:二叉树结构(红黑树结构)
线程不同步--不安全--效率高
TreeSet是如何保证元素的排序和唯一性的:
1: 元素对应的类,实现自然排序接口[Comparable],重写compareTo(obj1)方法
2: 或创建TreeSet集合对象时,实现比较器接口[Comparator], 重写compare(obj1, obj2)方法
Collection集合总结:
Collection
|- List
|- ArrayList(重点)
|- Vector (了解)
|- LinkedList (了解)
|- Set
|- HashSet (重点)
|- TreeSet(了解)
以后对于Collection集合,掌握如下:
考虑元素唯一 使用HashSet
可以有重复元素 使用ArrayList
案例:
1、ArrayList集合 嵌套 ArrayList集合
public class ArrayListTest {
public static void main(String[] args) {
//创建传智播客总部 集合
ArrayList<ArrayList<String>> czbk = new ArrayList<ArrayList<String>>();
//创建校区
ArrayList<String> bj = new ArrayList<String>();
//向北京校区添加学科
bj.add("Java");
bj.add("UI");
bj.add("Android");
ArrayList<String> sh = new ArrayList<String>();
//向北京校区添加学科
sh.add("Java");
sh.add("UI");
//总部添加 校区
czbk.add(bj);
czbk.add(sh);
System.out.println(czbk);
}
}
/2、编写一个程序,获取10个1至20的随机数,要求随机数不能重复
public class HashSetDemo {
public static void main(String[] args) {
//HashSet集合
HashSet<Integer> hs = new HashSet<Integer>();
//2: 判断当前HashSet集合中的元素个数 是否为10个
while (hs.size() != 10) {
// a: 创建一个1-20之间的随机数
int num = (int)(Math.random() * 20 + 1);
//b: 判断这个随机数是否在集合中存在
hs.add(num);
}
//3: 遍历集合
System.out.println(hs);
}
}
3、键盘录入3个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
public class TreeSetDemo {
public static void main(String[] args) {
// 2: 创建一个TreeSet<Student>集合 比较器方式
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 总分
int num = s2.getSum() - s1.getSum();
// 语文成绩
int num2 = (num == 0) ? (s2.getChineseScore() - s1
.getChineseScore()) : num;
// 数学成绩
int num3 = (num2 == 0) ? (s2.getMathScore() - s1.getMathScore())
: num2;
// 名字
int result = (num3 == 0) ? (s2.getName().compareTo(s1.getName()))
: num3;
return result;
}
});
// 3:创建3个学生对象
for (int i = 0; i < 3; i++) {
// 键盘输入
System.out.println("请输入姓名:");
String name = new Scanner(System.in).nextLine();
System.out.println("请输入语文成绩:");
int chineseScore = new Scanner(System.in).nextInt();
System.out.println("请输入数学成绩:");
int mathScore = new Scanner(System.in).nextInt();
System.out.println("请输入英语成绩:");
int englishScore = new Scanner(System.in).nextInt();
// 4:添加学生对象 到集合
ts.add(new Student(name, chineseScore, mathScore, englishScore));
}
// 5:遍历
System.out.println("-----------------------");
System.out.println("姓名\t" + "语文成绩\t" + "数学成绩\t" + "英语成绩\t" + "总分");
for (Student s : ts) {
System.out.println(s.getName() + "\t" + s.getChineseScore() + "\t"
+ s.getMathScore() + "\t" + s.getEnglishScore() + "\t" + s.getSum());
}
}
}
Map :
存储的是成对出现的元素
特点:
有键与值 组成是双列集合
键唯一, 值可以重复
Map集合的数据结构值针对键有效,跟值无关
Map接口和Collection接口的不同
Map是双列的,Collection是单列的
Map的键唯一,Collection的子体系Set是唯一的
Map集合的数据结构值针对键有效,跟值无关
Collection集合的数据结构是针对元素有效
方法:
V put(K key,V value) 添加键值对元素到集合
V remove(Object key) 根据指定的键,在集合中删除对应的键值对元素,返回键对应的值
void clear() 清空集合
boolean containsKey(Object key) 判断集合中是否包含给定的键
boolean containsValue(Object value) 判断集合中是否包含给定的值
boolean isEmpty() 判断集合是否为空
int size() 获取集合中键值对元素的个数
V get(Object key) 根据给定的键,获取对应的值
Set<K> keySet() 获取集合中所有的键
Collection<V> values() 获取集合中所有的值
Set<Map.Entry<K,V>> entrySet() 获取集合中所有的键值对元素对象
Map集合的遍历方式
方式1:根据键找值
获取所有键的集合
遍历键的集合,获取到每一个键
根据键找值
方式2:根据键值对对象找键和值
获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找键和值
HashMap:
底层: 哈希表结构
TreeMap:
底层: 二叉树结构
LinkedHashMap:
底层: 哈希表结构 + 链表结构
Collections:
集合工具类
public static <T> void sort(List<T> list) 排序
public static <T> int binarySearch(List<?> list,T key) 二分查找
public static <T> T max(Collection<?> coll) 最大值
public static void reverse(List<?> list) 反转
public static void shuffle(List<?> list) 随机打乱
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 替换
面试题:
HashMap和Hashtable的区别?
HashMap
线程不同步-- 不安全--效率高
可以使用null键 null值
Hashtable
线程同步 -- 安全 -- 效率低
不能使用null键 null值
案例:
1、统计字符串中每个字符出现的次数
public class TreeMapDemo {
public static void main(String[] args) {
//1: 定义一个字符串 "aababcabcdabcde"
String str = "aababcabcdabcde";
//2:定义一个集合 TreeMap<Character, Integer> 用来存储字母与次数
TreeMap<Character, Integer> map = new TreeMap<Character, Integer>(); // 自然排序
//3:遍历字符串,得到每一个字母
for (int i =0; i< str.length(); i++) {
//得到每一个字母
char ch = str.charAt(i);
//4:判断当前字母在集合中是否存在
if (map.containsKey(ch)) {
//把当前字母 在集合中存储的次数获取出来, 次数加1后,再存进去
int count = map.get(ch);
count++;
map.put(ch, count); //因为已存在,再次存入则为值得覆盖
} else {
//把当前字母 与 次数1 存进去
map.put(ch, 1);
}
}
System.out.println(map);
//5: 组装结果字符串 a(5)b(4)c(3)d(2)e(1)
//通过遍历Map集合得到每一个键与值 ,然后拼装
StringBuilder sb = new StringBuilder();
//方式2 键值对 找键 找值
Set<Entry<Character, Integer>> entrySet = map.entrySet();
//得到每一个键值对元素
for (Entry<Character, Integer> entry : entrySet) {
//找键 --- 字母
Character c = entry.getKey();
//找值 --- 次数
Integer n = entry.getValue();
sb.append(c).append("(").append(n).append(")");//a(5)
}
//打印结果
System.out.println(sb.toString());
}
}
2、集合嵌套之HashMap嵌套HashMap
public class HashMapDemo {
public static void main(String[] args) {
//1: 创建基础班HashMap集合 HashMap<String, String>
HashMap<String, String> jc = new HashMap<String, String>();
//2: 向基础班集合添加元素
jc.put("01", "张三");
jc.put("02", "李四");
//3: 创建就业班HashMap集合 HashMap<String, String>
HashMap<String, String> jy = new HashMap<String, String>();
//4: 向就业班集合添加元素
jy.put("01", "王五");
jy.put("02", "赵六");
//5: 创建传智播客HashMap集合 HashMap<String, HashMap<String, String> >
HashMap<String, HashMap<String, String>> czbk = new HashMap<String, HashMap<String, String>>();
//6: 向传智播客HashMap 添加元素
czbk.put("基础班", jc);
czbk.put("就业班", jy);
// 键 找 值
//所有的班级名称
Set<String> classeNames = czbk.keySet();
for (String classeName : classeNames) {
//每一个班级名称
System.out.println(classeName);
//通过班级名称,找到对应班级的所有学生信息
HashMap<String, String> students = czbk.get(classeName);
//获得当前班级中所有学生的学号
Set<String> ids = students.keySet();
//得到每一个学生的学号
for (String id : ids) {
//通过学号获取对应姓名
String name = students.get(id);
System.out.println("\t"+id + "---"+ name);
}
}
}
}