黑马程序员:集合的学习

本文深入讲解Java集合框架的原理和应用,包括集合的由来、集合与数组的区别、集合框架的体系结构等内容,并通过具体案例介绍了List、Set、Map等接口的使用方法。

------- android培训java培训期待与您交流! ---------- 

集合的由来:
java是一种面向对象的语言,对现实世界中的事物均以对象体现,为了方便对多个对象进行操作(对象数组的局限性)
为了方便我们对多个对象进行操作,java是提供了一种新的容器,就是集合类(框架)
   :用于存储对象的容器
  : 该容器的长度是可变的
   :集合中存储的对象可以是任意类型的

集合与数组的区别:
   集合:
  长度是可变的
集合只能存储对象类型
集合可以存储多种引用数据类型的元素
   数组:
长度是固定的
数组可以存储对象类型,也可以存储基本数据类型
数组存储的多个元素是同一种数据类型
由于数据在存储的时候,数据结构不一样,所以,就会出现多种类型的集合方便我们操作。
  不管你是哪种类型的集合,都是满足一些常见的操作

集合框架的由来:(理解)
由于多种集合类的数据结构不同,通过不断的向上抽取,形成了集合的体系结构。
学习和使用体系结构的方式。
学习顶层,因为顶层定义的是共性的东西。
使用底层,因为底层是具体实现的东西。
数据结构:
组织数据的方式。
但是,操作的方式又不一样,这个时候,就要向上抽取,最终形成一个集合体系结构

Collection接口功能: (重点,能够把数据放到集合中,然后把数据从集合中遍历出来)
1、添加元素:
boolean add(Object obj);//添加单个元素
boolean addAll(Collection c);添加多个元素
2、判断元素:
boolean contains(Object obj);判断元素是否存在。
boolean containsAll(Collection c);判断一个集合的元素是否包含在当前集合中
boolean isEmpty();判断集合是否为空
3、删除元素:
void clear();移除所有元素。这个方法太暴力了。
boolean remove(Object o);移除单个元素。
boolean removeALL(Collection c);移除多个元素。
4、获取元素:
Iterator iterator();返回集合上的一个迭代器
5、交集:
boolean  retainAll(Collection c)交集
6、集合元素的个数:
  int size();元素个数
7、把集合转成数组
Object[] toArray();   建议不要使用此方法进行集合的遍历
迭代器的使用:
   Iterator iterator();
         迭代器中的三个方法:
  boolean hasNext(); 判断是否还有元素
  Object next():获取元素,并且还可以自动指向下一个元素
  void remove(); 删除元素:
原理: 每种集合的存储方式不一样,所以,它的取出方式也不一样
但是,他们都应该有获取的功能,所以,进行了抽取
当你遍历某种类型的集合的时候,就由该类型的集合自己去实现迭代器接口
*************************************
集合操作的思路:
A:创建集合对象
B:创建元素对象
C:把元素添加到集合中
D:遍历集体
**通过集合对象获取迭代器对象
**调用迭代器对象的hasNext()方法判断是否还有元素
**调用迭代器对象的next()方法获取元素。
例如:
    class IteratorDemo 
{
public static void main(String[] args) 
{
//创建集合对象
Collection c=new ArrayList();
//添加元素
c.add("hello");
c.add("world");
c.add("itcast");
System.out.println(c);
//遍历
//用数组实现的,这种不用Object[]  toArray()
//迭代器的实现
Iterator it=c.iterator(); //返回的是实现了迭代器接口的子类对象
//获取元素
while(it.hasNext())//判断是否还有元素
{
//
  Object obj=it.next()//可以直接由Object类的进行接收
System.out.println(it.next()); //可以直接打印出来
}
}
}
例2:
*
 * 集合存储自定义对象
 */
public class CollectionDemo {
public static void main(String[] args) {
// 创建集合对象
Collection c = new ArrayList();
// 创建元素对象
Student s1 = new Student("东方不败", 20);
Student s2 = new Student("林平之", 18);
Student s3 = new Student("岳不群", 19);
// 添加元素
c.add(s1);
c.add(s2);
c.add(s3);
// 遍历元素
// 通过集合对象获取迭代器对象
Iterator it = c.iterator();
// 通过hasNext()方法判断
while (it.hasNext()) {
// 通过next()方获取元素
// System.out.println(it.next().toString());
Student s = (Student) it.next();
// System.out.println(s);
System.out.println(s.getName() + "***" + s.getAge());
}
}
}
-----------------------List
Collection 
|----List
元素有序(存入顺序和取出顺序一致),元素可以重复


|---Set
元素是无序,元素要求唯一
List接口的功能:
1、添加元素
void add(int index,Object obj);指定位置添加指定元素
2、获取元素:
Object get(int index):根据指定索引获取元素
3、列表迭代器
ListIterator  listIterator();
4、删除元素:
Object remove(int index);删除指定位置的元素
5、修改元素:
Object set(int index,Object element);修改指定位置的元素
size()方法,它可以获取集合的元素个数。
--------------------------------------------------------
例:get方法可以根据指定的索引获取单个元素
   那么List体系的集合就多了一种遍历方式:
  我们想到了size()方法,它可以获取集合的元素个数
这样的话,我们就可以从0开始获取每个元素的索引
 public class ListDemo2
{
  //创建集合对象
List list=new new ArrayList();
  //添加元素
list.add();
//遍历
  for(int x=0;x<list.size();x++)
{
       //Stirng s=(String)lis.get(x);
     System.out.println(list.get(x));
}
// 这种方法效率不高,可以优化
// list.size()每次循环,都去调用了方法计算集合的元素个数
// 如果元素过多,这种方式不好
// int length = list.size();
// for (int x = 0; x < length; x++) {
// String s = (String) list.get(x);
// System.out.println(s);
// }
在集合元素比较多的时候 可以使用此方法
}
---------list迭代器的使用
 Iterator
hasNext()
next();
----ListIterator  listIterator();列表迭代器遍历集合元素
    有自己的特殊功能,可以实现在逆向遍历
1、Object previous();  //读取一个元素
  2、boolean hasPrevious();//判断是否有下一个元素
例1:
   Iterator it=it.iterator();
   while(lit.hasPrevious())
{
    String s=(String)lit.previous();
System.out.println(s);
}
例2:
 * 要查找集合中有没有是zhangsan这个字符串。
 * 如果有,则在添加一个lisi进去。
 * 
 * Exception in thread "main" java.util.ConcurrentModificationException:并发修改异常
 * 在使用迭代器迭代元素的时候,不能使用集合去操作元素,否则,就会出现并发修改异常。
 * 那么,请问如何解决这个问题,并能够让元素添加成功:
 * 1:在迭代器迭代的过程中,使用迭代器对象去添加元素。
 * 2:不用迭代器遍历,用普通for循环遍历,用集合添加元素
 */
public class ListDemo4 {
public static void main(String[] args) {
// 创建集合对象
List list = new ArrayList();
list.add("zhaoliu");
list.add("zhangsan");
list.add("wangwu");
// 请问如何实现我的需求
ListIterator lit = list.listIterator();
while (lit.hasNext()) {
String s = (String) lit.next();
// 判断
if ("zhangsan".equals(s)) {
// void add(E e)
lit.add("lisi");  //只能使用迭代器进行数据的添加
// list.add("lisi");
}
}

/*
for(int x=0; x<list.size();x++){
String s = (String)list.get(x);
if("zhangsan".equals(s)){
list.add("lisi");
}
}
*/
System.out.println("list:" + list);
}
}
  
-----------------------------------------------List集合的使用------------------------------------------------------------------------
list的儿子的特点:
        |---ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
|---Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
|---LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
使用总结:
我们开发中到底使用哪个子类呢
 是否考虑线程安全问题 
  考虑:
使用Vector
 不考虑:
使用ArrayList 或者LinkedList
如果查询多,增删少,用ArrayList
如果查询少,增删少,用LinkedList
如果你还是不知道 ,用ArrayList


 // 方式3
//注意:如果使用列表迭代器,我们虽然说了它可以逆序遍历。
//但前提是先正向遍历到结尾了。然后才能逆序遍历。
ListIterator lit = array.listIterator();
while (lit.hasPrevious()) {
String s = (String) lit.previous();
System.out.println(s);  
Vector的特殊方法:
 1:添加元素
 * void addElement(Object obj) -- JDK1.2 后改为了 add(Object obj)
2: 获取元素
 * Object elementAt(int index) -- JDK1.2后就改为了 get(int index)
3:遍历元素
  Enumeration elements() -- JDK1.2后就改为了 Iterator iterator()
  boolean hasMoreElements() --   JDK1.2后就改为了 boolean hasNext()
  Object nextElement() -- JDK1.2后就改为了 Object next()
LinkedList的特殊方法:
1:添加元素
  void addFirst(Object obj)
  void addLast(Object obj)
  2:删除元素
  Object removeFirst()
Object removeLast()
  3:获取元素
Object getFirst()
  Object getLast()
 泛型:是一种特殊的类型,它把指定类型的工作推迟代码声明并实例化类或方法的时候进行。
  泛型的体现:<数据类型>
  在我们使用泛型之前,代码会有黄线警告,还可能会出现类型转换异常。
  我们肯定要解决这个问题,那么,怎么解决呢?
  我们在想想我们曾经使用的数组:
  String[] strArray = new String[3];
  strArray[0] = "hello";
  strArray[1] = "world";
  strArray[2] = 10;
  
  for(int x=0; x<strArray.length; x++)
  {
  String s = strArray[x];
  System.out.println(s);
  }
  通过这个例子,我们看到,这里已经出问题了,这个时候,我们需要改代码。
  通过这个例子我们还发现这个数组里面到底存储什么类型的数据,我一开始就知道了。
  这样的话,我们存入数据的时候,如果类型不匹配,立马就报错了,我们就马上改正了。
  还有第二点,这样可以避免运行时期的类型转换异常。
  
  泛型的好处:
  1:将原先时期出现的ClassCastException问题,转移到了编译时期。
  2:泛型的出现避免了强制转换的麻烦。
  3:优化了程序设计
      
----------------------------------------------------Set集合------------------------------------------------
1:Set(掌握)
(1)Set集合的特点:元素无序,唯一(掌握)
(2)Set的体系结构(掌握)
Set
|--HashSet
底层数据结构是哈希表。
如何保证元素的唯一性呢?
它依赖两个方法:hashCode()和equals()
首先判断哈希值是否相同:
不同:就把元素添加到集合中。
相同:继续进入equals方法比较
返回true,说明元素重复,不存。
返回false,就把元素添加到集合中。
|--TreeSet
底层数据结构是二叉树。
可以让集合中的元素排序。
如何保证元素的唯一性的呢?
它是根据比较返回的值是0,说明元素是重复的,就不添加。
有两种实现方案:
A:让自定义对象具备比较性
实现Comparable接口
B:让集合具备比较性
实现Comparator接口

如果两种情况都存在,以B为主。
(3)案例:(掌握)
用HashSet存储自定义对象,并去掉重复元素
用TreeSet存储自定义对象,并按需求排序和去掉重复值
(4)自学LinkedHashSet(了解)
底层数据结构是由哈希表和链表组成。
特点:
有序,唯一
请用一个案例验证总结的特点。


2:泛型(理解)
(1)泛型类
(2)泛型方法
(3)泛型接口
(4)重点掌握集合中添加泛型的使用。


3:增强for(掌握)
(1)好处:简化了数组和Collection集合的遍历。(掌握)
(2)格式(掌握)
for(Collection集合或者数组对象的元素类型 变量 : Collection集合或者数组对象)
{
//使用变量即可
}
(3)案例:(掌握)
能够使用增强for遍历数组,Collection集合。

例如:    
需求:我认为成员属性相同对象即为同一个对象。
 * 
 * 我们重写了equals方法,发现没有达到效果。
 * 通过测试,发现根本没有执行equals方法。
 * 那么,问题出现在了什么地方呢?
 * 通过思考,我们估计问题是出现在了add方法上。
 * 所以,我们要研究add方法的源码。
 * 通过查看源码,我们发现这个add方法和hashCode()及equals()有关。
 * 而且是当哈希值相同的时候,采取执行equals方法。
 * 
 * HashSet是如何保证元素的唯一性的呢?
 * 底层数据结构是哈希表。
 * 哈希表依赖的是哈希值存储数据的。
 * 这个集合首先会根据hashCode方法去判断哈希值是否相同,
 * 如果不同,则认为是不同的元素,就添加到集合中。
 * 如果相同,就会走equals方法,这样就可以实现根据自己的需求进行比较。
 * 如果返回值是true,说明该元素在集合中存在,就不添加。
 * 如果返回值是false,直接添加元素。
 */
public class HashSetDemo {
public static void main(String[] args) {
// 创建集合对象
HashSet<Person> hs = new HashSet<Person>();
// 创建元素对象
Person p1 = new Person("林青霞", 25);
Person p2 = new Person("张曼玉", 28);
// 添加元素
hs.add(p1);
hs.add(p2);
// 遍历
Iterator<Person> it = hs.iterator();
while (it.hasNext()) {
Person p = it.next();
System.out.println(p.getName() + "***" + p.getAge());
}
}
}
例如:
我们的TreeSet集合是用于保证元素排序和唯一的。
 * 而我们现在的这种操作,并没有让集合中的元素进行排序。
 * 通过运行时期的异常,我们可以知道解决
 * 方案1:让自定义对象实现Comparable接口
 public int compareTo(Person p) {
// 小-大 (this - p)
// 大-小(p - this)
// 按照姓名的从短到长,年龄从大到小顺序排序,并去掉重复元素(姓名和年龄相同即为重复元素)。
// 谁调用this代表谁
int num = this.name.length() - p.name.length();
int num2 = (num == 0) ? (p.age - this.age) : num;
// 追加条件,这个条件是需要我们自己思考的
int num3 = (num2 == 0) ? (this.name.compareTo(p.name)) : num2;
return num3;
}。
 * 方案2:让集合具备比较性
 * 使用TreeSet带比较器的构造方法
 * 
 * 当Person类具备比较性的时候,是指Person类实现Comparable接口
 * 集合也具备比较性的时候,是指TreeSet接受了实现Comparator接口的子类对象
 * 以集合具备比较性为主。这个时候,也就是说自定义对象没有必要再去实现比较性的接口。
 * 
 * TreeMap -- put
 * 
 * 需求:成员属性相同的元素为同一对象。我想让元素按照年龄从大到小排序。
 *  
 * TreeSet保证元素唯一性的原理?
 * 根据比较方法返回的值是否是0来确定该元素是否是重复元素。
 * 
 * 在用TreeSet保证元素唯一的时候,我们还得考虑元素的排序。
 * 而排序的时候,一定要注意排序的主次要条件。
 *
 */
public class TreeSetDemo3 {
public static void main(String[] args) {
// 创建集合对象
// 以后注意,如果一个方法的参数是一个接口类型的
// 那么,你肯定传递的是一个实现了接口的子类对象
// 匿名对象的使用
TreeSet<Person> ts = new TreeSet<Person>(new MyComparator());
// 创建元素对象
Person p1 = new Person("陆毅", 30);
// 添加元素
ts.add(p1);
// 遍历元素
Iterator<Person> it = ts.iterator();
while (it.hasNext()) {
Person p = it.next();
System.out.println(p.getName() + "***" + p.getAge());
}
}
}
-------------------------------------- 增强for循环-------------------------------------------------
 * 格式:
 * for(Collection集合或者数组对象中元素的数据类型 变量名 : Collection集合或者数组对象)
 * {
 * 在这个里面直接使用这个变量,其实就是依次获取到的集合或者数组中的元素。
 * }
 * 
 * 增强for就是为了方便对Collection集合或者数组的遍历。
 * 也是可以用于替代迭代器的。
 */


-------------------------------------------------:Map(掌握)-----------------------------------------------
(1)Map:存储的是键值对形式的数据的集合。(了解)
(2)Map的特点:(了解)
数据是以键值对形式存在
键不能是重复的
值可以重复
(3)Map接口的功能:(掌握)
A:添加元素
V put(K key,V value)
B:判断元素
boolean containsKey(K key)
boolean containsValue(V value)
boolean isEmpty()
C:删除元素
V remove(K key)
D:长度
int size()
E:获取
V get(k key)
Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V>> entrySet()
(4)Map案例:(掌握)
Map存储字符串并遍历:
Map<String,String> map = new HashMap<String,String>();
map.put("it001","zhangsan");
map.put("it002","lisi");
map.put("it003","wangwu");
//遍历
//方式1:丈夫找妻子
Set<String> set = map.keySet();
for(String key : set)
{
String value = map.get(key);
System.out.println(key+"***"+value);
}
//方式2:通过结婚证找丈夫和妻子
Set<Map.Entry<String,String>> entrySet = map.entrySet();
for(Map.Entry<String,String> me : entrySet)
{
String key = me.getkey();
String value = me.getValue();
System.out.println(key+"***"+value);
}
(5)Map的体系结构(掌握)
Map:(Map体系的数据结构对键有效,跟值无关)
|--HashMap
底层数据结构是哈希表。
如何保证键的唯一性呢?
依赖hashCode()和equals()方法
线程不安全,效率高。允许null键和值。
|--Hashtable
底层数据结构是哈希表。
如何保证键的唯一性呢?
依赖hashCode()和equals()方法
线程安全,效率低。不允许null键和值。
|--TreeMap
底层数据结构是二叉树。
如何保证键的唯一性呢?
两种方式:
自定义元素具备比较性
集合具备比较性
线程不安全,效率高。允许null值,不允许null键。

那么,我们一般使用谁?
如果有排序需求,用TreeMap,否则全部使用HashMap。
(6)案例:
统计字符串中每个字符出现的次数(掌握)
czbk集合的数据存储和遍历(理解)

2:总结集合的使用规律:
是否是键值对形式:
是:Map体系
是否需要排序:
是:TreeMap
不是:HashMap
不知道:HashMap
不是:Collection体系
是否要保证元素唯一:
是:Set
是否要排序:
是:TreeSet
不是:HashSet
不知道:HashSet
不是:List
查询多:ArrayList
增删多:LinkedList
不知道:ArrayList
3:遍历方式:
Collection:
List:
普通for
增强for
//迭代器
Set:
增强for
//迭代器
Map:没有直接遍历方式,需要通过转换。
方式1:丈夫找妻子
//方式2:通过结婚证找丈夫和妻子
4:集合的底层数据结构规律:
ArrayXxx:底层数据结构是数组,查询快,增删慢。
LinkXxx:底层数据结构是链表,查询慢,增删快。
HashXxx:底层数据结构是哈希表。
依赖hashCode()方法和equals()方法
TreeXxx:底层数据结构是二叉树
两种方式实现排序:
自定义元素具备比较性
集合具备比较性


 

------- android培训java培训期待与您交流! ---------- 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值