黑马程序员之 --- 集合

来源:互联网 发布:php后端开发教程 编辑:程序博客网 时间:2024/06/05 02:44

------- <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 QQTE

泛型常见在哪里可以使用?

     接口上使用泛型

     类上使用泛型

     方法上使用泛型

泛型类:

把泛型定义在类上

格式: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 <泛型类型返回类型 方法名(泛型类型 .)

的类型是在创建对象的时候 确定的

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);

}

}

3ArrayList存储自定义对象并遍历增强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 

案例:

1ArrayList集合 嵌套 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编写一个程序,获取10120的随机数,要求随机数不能重复

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) 替换

面试题:

HashMapHashtable的区别?

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 {

//把当前字母  与 次数存进去

map.put(ch, 1);

}

}

System.out.println(map);

//5: 组装结果字符串  a(5)b(4)c(3)d(2)e(1)

//通过遍历Map集合得到每一个键与值 ,然后拼装

StringBuilder sb = new StringBuilder();

//方式键值对 找键 找值

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);

}

}

}

}

 

 

 

    


0 0