黑马程序员---java之集合框架

时间:2023-02-18 15:15:27
java之集合框架

 

                        

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


集合类           面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个         对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。   数组与集合类的区别           数组和集合类都是容器,但是数组的长度是固定的,集合长度是可变的          数组中可以存储基本数据类型,集合只能存储对象,   集合类的特点           集合之用于存储对象,集合长度是可变的,集合可以存储不同哦你类型的对象   集合图解析   黑马程序员---java之集合框架

 

 


集合框架共性方法

集合类是接口,故而没有构造方法。主要的方法有:aad(E e)、addAll(Collection<? extends E> c)、clear()、contains(Objiect o)、containsAll(Collection <?> c)、equals(Objiect o)、hashCode()、isEmpty()、iterator()、remove(Objiect o)、removeAll(Collection <?> c)、retainAll(Collection <?> c)、size()、toArray()、toArray(T[] a)。如何使用这些方法呢?用代码解释:

 import java.util.*;
public class CollectionDemo
{
 public static void main(String[] args)
 {
  //创建一个集合容器,使用Collection接口子类:ArrayList
  ArrayList class4= new ArrayList();
//  1.添加元素
  class4.add("刘备");
  class4.add("李程");
  class4.add("蒋望君");

  class4.add("石焕");
//  打印原集合
  sop("原集合:" + class4);
//  获取个数,集合长度
  sop("size:" + class4.size());
//  2.删除元素
  class4.remove("李程");
//  打印集合
  sop("改变后的:" + class4);
//  获取个数,集合长度
  sop("size:" + class4.size());

//  3.判断元素
  sop("石焕是否存在:" + class4.contains("石焕"));

  sop("集合是否为空:" + class4.isEmpty());

//  4.清空集合
  class4.clear();
//  打印元素
  sop("清空后的:" + class4);
//  获取个数,集合长度
  sop("size:" + class4.size());
 
 
 public static void sop(Object obj)
 {
  System.out.println(obj);
 }
 
}

 

其结果为:

原集合:[刘备, 李程, 蒋望君, 石焕]
size:4
改变后的:[刘备, 蒋望君, 石焕]
size:3

石焕是否存在:true

集合是否为空:false
清空后的:[]
size:0

 

值得注意的是:

1、add方法中的参数类型是Objiect,以便于接收任意类型对象。

2、集合中存储的都是对象的应用(地址)。

 

用另一段代码说明另两种方法的使用:

import java.util.ArrayList;

public class CollectionMethod
{
 public static void main(String[] args)
 {
  ArrayList class4 = new ArrayList();
  class4.add("刘备");
  class4.add("李程");
  class4.add("蒋望君");
  class4.add("石焕");
  
  ArrayList hjj = new ArrayList();
  hjj.add("刘备");
  hjj.add("李程");
  hjj.add("郑强");
  hjj.add("朱琳");
//  去交集,class4中只会保留与hjj中不同的元素
  class4.removeAll(hjj);
  sop("class4:" + class4);
  sop("hjj:" + hjj);

 

  class4.add("刘备");
  class4.add("李程");

//  取交集,class4中只会保留与hjj中相同的元素
  class4.retainAll(hjj);
  sop("class4:" + class4);
  sop("hjj:" + hjj);
 }
 public static void sop(Object obj)
 {
  System.out.println(obj);
 }
}

 

其结果为:

class4:[蒋望君, 石焕]
hjj:[刘备, 李程, 郑强, 朱琳]
class4:[刘备, 李程]
hjj:[刘备, 李程, 郑强, 朱琳]

 

迭代器

 

 迭代器是通过内部类实现,该对象必须依赖于具体的容器,因为每个容器的数据结构都不同, 所以该迭代器对象是在容器内部实现的。

 对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法

 Iterate接口就是对所以Collection容器进行元素取出的公共接口。

示例代码:

public class jihe{

public static  void main(String args[]){

Collection coll = new ArrayList();

coll.add("abc1");

coll.add("abc2");

coll.add("abc3");

//使用了Collection 中的iterator()方法,调用集合中的迭代器方法

//是为了获取集合的迭代器对象

//用while,循环结束后,it.next()还能用,但是会浪费内存

Iterator it = coll.iterator();

while(it.hasNext()){

System.out.println(it.next());

}

//用for循环,可以节约内存,开发时一般使用

for(Iterator it = coll.iterator();it.hasNext();){

System.out.println(it.next());

}

}

 }

 

 

List集合的共性方法

Collection下有List和Set
List中的元素是有序的,元素可以重复,因为该集合体系有索引
Set元素是无序的,不可以重复。

List集合中特有方法:

记住凡是可以操作角标的方法都是特有的方法

增:
add(index,obj)在指定位置插入元素。
addAll(index,Collection)添加一个集合进去
删:
remove(index)移除指定位置的元素。
改:
set(index,obj)修改指定位置元素
查:
get(index)
for(int i=0;i<al.size();i++)
{
 al.get(i);
}
迭代器获取:
Iterator it=al.iterator();
while(it.hasNext())
{
 System.out,println(it.next());
}
//通过indexOf获取对象的位置
al.indexOf(obj);
//截取字串
al.subList(start,end);

List集合具体对象的特点

List集合子对象:ArrayList,LinkedList,Vector。
ArrayList底层使用的是数组结构,特点在于查询速度很快,但是增删稍慢。线程不同步
LinkedList使用的是链表结构。特点就是增删速度很快,查询很慢。
Vector:底层是数组数据结构,出现较早。1.0版本就出现了。线程不同步。无论增删还是查询都慢。被ArrayList取代了。

 

Vector中的枚举

Vector v=new Vector();
v.add("java01");
v.add("java02");
v.add("java03");
v.add("java04");
Enumeration en=v.elements();
while(en.hasMoreElements())
{
 System.out.println(en.nextElement());
}

枚举就是Vector特有的取出方式。
发现枚举和迭代器很像。
其实枚举和迭代是一样的。
因为枚举的名称和方法的名称都长,所以被迭代器取代了,枚举郁郁而终了。

 

LinkedList

特有方法:
addFirst()往头部添加
addLast()往尾部添加
getFirst()获取头部
getLast()获取尾部
removeFirst()移除头部
removeLast()移除尾部

get是只取不删,remove不仅取还删除。

//取出元素
while(!link.isEmpty())
{
 link.removeFirst();正着取
 link.removeLast();倒着取
}

当没有元素时,使用以上方法会返回没有元素异常NoSuchElementException。

poolFirst()获取并移除第一个元素,如果不存在元素,会返回null
peekFirst()获取但不移除第一个元素,如果不存在元素,会返回null

 

ArrayList

1.ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了动态的增加和减少元素,实现了ICollection和IList接口,灵活的设置数组的大小等好处。

2.ArrayList的定义

List 接口的大小可变数组的实现,位于API文档的java.util.ArrayList<E>。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)   size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。   每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单   在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:   List list = Collections.synchronizedList(new ArrayList(...)); 此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。   注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。  

3.常用的方法

boolean add(E e) 将指定的元素添加到此列表的尾部。 void add(int index, E element) 将指定的元素插入此列表中的指定位置。 boolean addAll(Collection<? extends E> c) 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。 boolean addAll(int index, Collection<? extends E> c) 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。 void clear() 移除此列表中的所有元素。 Object clone() 返回此 ArrayList 实例的浅表副本。 boolean contains(Object o) 如果此列表中包含指定的元素,则返回 true。 void ensureCapacity(int minCapacity) 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。 E get(int index) 返回此列表中指定位置上的元素 int indexOf(Object o) 返回此列表中首次出现的指定元素索引,或如果此列表不包含元素,则返回 -1。 boolean isEmpty() 如果此列表中没有元素,则返回 true int lastIndexOf(Object o) 返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。 E remove(int index) 移除此列表中指定位置上的元素。 boolean remove(Object o) 移除此列表中首次出现的指定元素(如果存在)。 protected void removeRange(int fromIndex, int toIndex) 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素 E set(int index, E element) 用指定的元素替代此列表中指定位置上的元素。 int size() 返回此列表中的元素数。 Object[] toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素数组 <T> T[] toArray(T[] a) 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 void trimToSize() 将此 ArrayList 实例的容量调整为列表的当前大小。  

HashSet

Set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复。
Set集合的功能和Collection是一致的。
Set常见的子类:
HashSet:底层数据结构是哈希表
TreeSet:
Set集合的读取只有一种方式就是迭代器Iterator

 

HashSet存储自定义对象

HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值不同,才会判断equals是否为true
如果元素的hashcode值不同,不会调用equals

 

HashSet判断和删除的依据

HashSet hs=new HashSet();
hs.contains(obj);
hs.remove(obj);

注意:对于判断元素是否存在,以及删除等条件,依赖的是元素的hashCode和equals方法。

 

TreeSet

Set:无序,不可以重复元素
 HashSet:数据结构是哈希表,线程时非同步的。
 保证元素唯一性的原理,判断元素的hashcode是否相同,如果相同,还会继续判断元素的equals方法,是否为true。
 TreeSet:可以对set集合中的元素排序
 底层数据结构是二叉树,保证元素唯一性的依据:compareTo方法return 0;
 TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Compareable接口,覆盖compareTo方法,这种顺序也叫自然顺序,也叫默认顺序。

TreeSet存储自定义对象

TreeSet里存储的自定义对象的类必须实现Comparable接口,并覆盖compareTo(Object obj)方法。
该方法返回负数,零或正数,分别代表该对象小于,等于或大于该对象。

记住:排序时,当主要条件相同时,一定要判断一下次要条件。

 

实现Comparator方式排序

TreeSet集合的第二种排序方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的,这时需要让集合自身具备比较性。

在集合一初始化时,就有了比较方式。
定义一个比较器,
class MyCompare implements Comparator
{
 public int compare(Object o1,Object o2)
 {
  排序方式;
 }
}
然后在创建TreeSet对象时,传进一个MyCompare对象
TreeSet TS=new TreeSet(new MyCompare());
那么这个TS就会以实现Comparator接口的MyCompare类的对象的排序方式进行排序。
 
总结两种比较方式:一是让容器自身具有比较性,容器初始化时实传入Comparator接口对象;二是让对象具有比较性,对象实现Compareable接口。

 

泛型概述

jdk1.5版本之后出现新特性,用于解决安全问题的,是一个安全机制。

如下:只能存储String的ArrayList
ArrayList<String> AL=new ArrayList<String>();
//取出的时候也要用泛型
Iterator<String> It=AL.iterator();
while(It.hasNext())
{
 取出元素:It.next();
}


好处一:
将运行时期的出现的问题ClassCastException转移到了编译时期,方便程序员解决问题,让运行时的问题减少,安全。
2.避免了强制转换麻烦。

 

泛型使用

泛型格式:通过<>来定义要操作对象的引用类型。

在使用java时,什么时候可以使用泛型呢?
通常在集合框架中很常见,只要见到<>就可以使用。
其实<>是用来接收类型的,当使用集合时,将集合中要存储的数据类型作为参数传递到<>即可。
泛型只能接受引用数据类型。 

 

泛型类

没有泛型时的工具类:
class Tool
{
 private Object obj;
 public void setObject(Object obj)
 {
  this.obj=obj;
 }
 public Object getObject()
 {
  return obj;
 }
}
泛型出现后的工具类:
class Tool<QQ>
{
 private QQ q;
 public void setObject(QQ q)
 {
  this.q=q;
 }
 public QQ getObject()
 {
  return q;
 }
}
这就是传说中的泛型类。

什么时候需要泛型类:当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展。

 

泛型方法

class Demo<T>
{
 public void show(T t)//该方法的参数类型随着该类的泛型走
 {
  
 }
 public <Q> void print(Q q)//泛型方法,该方法的参数类型是Q
 {
  
 }

泛型类定义的泛型在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所以要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定,那么就可以将泛型定义在方法上。


 

泛型静态方法

记住:特殊之处:
静态方法不可以访问类上定义的泛型,如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。

public static <W> void method(W w)
{
 


 

泛型接口

泛型定义在接口上
interface Inter<T>
{
 void show(T t);

class InterSon<T> implements Inter<T>
{
 public void show(T t)
 {
  
 }
}

 

泛型限定

通用的泛型:如ArrayList<?> al;

public static <T> void print(ArrayList<T> al)
{
 Iterator<T> it=al.iterator();
 while(it.hasNext())
 {
  打印;
 }

通用泛型可以把上面的T换成?号。但要注意传入对象的特有方法。

泛型限定:
?:通配符,也可以理解为占位符。 
<? extends E>:可以接受E类型或E的子类型,这是上限固定。
<? super E>:可以接受E类型或者E的父类型。这是下限固定。

 

Map概述

Map集合的基本特点:
该集合存储键值对,是一对一对往里存,而且要保证键的唯一性。

添加:
put(key,value)
putAll()
删除:
clear();
remove(Object obj);按键值删除
判断:
containsKey(Object obj)
containsValue(Object obj)
isEmpty()
获取:
get(Object obj)
size()
values();返回的是Collection<T>

entrySet()
keySet()

 

Map子类对象的特点

HashTable:底层是Hash表数据结构,不可以存入null值和null键。该集合是线程同步的。jdk1.0.效率低。
HashMap:底层是hash表数据结构,允许使用null键和null值,该集合是不同步的。jkd1.2.效率高。
TreeMap:底层是二叉树数据结构,线程不同步,可以用于给map集合中的键进行排序。和Set很像,其实Set底层就是使用了Map集合。

 

Map---keySet

Map集合的两种取出方式:
第一种keySet:将map中所有的键存入Set集合,因为Set具备迭代器,所有可以迭代方式取出所有的键,获取每一个键对应的值。

Map<String,String> map=new Map<String,String>();

map.put("02","zhangsan2");
map.put("03","zhangsan3");
map.put("04","zhangsan4");
map.put("05","zhangsan5");

Set<String> keySet=map.keySet();

Iterator<String> it=keySet.iterator();

while(it.hasNext())
{
 String key=it.next();
 String value=map.get(key);
 输出键和值;
}

map集合的取出原理:将map集合转成set集合,再通过迭代器取出。

 

Map---entrySet

第二种entrySet:
Set<Map.Entry<k,v>> entrySet:将map集合中的映射关系存入到set集合中,而这个关系的数据类型就是:Map.Entry。

Set<Map.Entry<String,String>> entrySet=map.entrySet();

Iterator<Map.Entry<String,String>> it=entrySet.iterator();
while(it.hasNext())
{
 Map.Entry<String,String> me=it.next();
 String key=me.geyKey();
 String value=me.getValue();
 这样就取出来了;
}

Map.Entry其实Entry也是一个接口,它是Map接口中的一个内部接口。
interface Map
{
 public static interface Entry
 {
  public abstract Object getKey();
  public abstract Object getValue();
 }
}

 

Map扩展

map集合被使用是因为具备映射关系。
HashMap<String,String> hm0=new HashMap<String,String>();
HashMap<String,HashMap<String,String>> hm1=new HashMap<String,HashMap<String,String>>();

 

Collections---sort

public static <T extends Comparable<? super T>> void sort(List<T> list)

{

      

}

Collections.sort(List<T> list);:把List集合按自然顺序排序。

Collections.sort(List<T> list,Comparator<? super T> com);:按比较器进行排序。

比较器定义:

class ComparatorDemo implements Comparator<T>

{

       //覆盖

       public int compare(T t1,T t2)

       {

             

       }

}

  

Collections---binarySearch

Collections.binarySearch(List<T> list,T Key)

{

      

}

返回key在list中的角标。

 

Collections---替换反转

Collections.fill(List<T> list,T Key)

将list集合中的所有元素替换成指定元素。

 

练习:将集合中的部分元素替换成指定元素

 

replaceAll(List<T>,T oldValue,T newValue);

使用另一个值替换表中出现的某一指定值。

reverse(List<T>):将list中的元素反转。

 

Collections---reverseOrder

TreeSet<String> ts=new TreeSet<String>(Collections.reverseOrder());

Collections.reverseOrder():按自然顺序强行逆转排序。

Collections.reverseOrder(现有比较器):按现有比较器强行逆转排序

 

 

Collections---SynList

synchronizedSet(Set<T> set)

synchronizedList(List<T> list)

swap(List<T> list,int i,int j):直接交换list集合中指定位置的元素

 

Arrays

Arrays:用于操作数组的工具类,里面都是静态方法。

asLsit():将数组变成List集合。

为什么把数组变成List集合:可以使用集合的思想和方法来操作数组中的元素。

注意:将数组变成集合,不可以使用集合的增删方法。

如果增删了,那么会发生不支持操作异常。UnsupportedOperationException。

若果数组中的元素都是对象,变成集合时,数组中的元素就直接转成集合中的元素。

如果数组中的元素,都是基本数据类型,会将该数组作为集合中的元素存在。

 

集合转成数组

Collection接口中的toArray方法。

 

1.指定类型的数组到底要定义多长呢?当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的siae。

当指定类型的数组长度大于了集合的size,就不会创建数组,而是使用传递进来的数组。

所以创建一个刚刚好的数组最优。

为什么要将集合变数组?

其实是为了限定对元素的操作不需要进行增删了。

 

增强for循环

高级for循环

格式:

for(数据类型 变量名:被遍历的集合或数组)

{

      

}

ArrayList(String) al=new ArrayList<String>();

for(String s:al)

{

      

}

 

这个循环是有局限性的,只能取出,不能修改或移除。

 

对集合进行遍历只能获取,但是不能对集合进行操作。

迭代器除了遍历,还可以进行remove集合中元素的动作。

如果使用listIterator。还可以在遍历中对集合进行增删改查的动作。

 

传统for循环和高级for有什么区别?

高级for有一个局限性,必须有被遍历的目标。

在遍历数组的时候还是希望使用传统for,因为传统for有角标。

凡是支持迭代器的集合都支持高级for循环。

 

可变参数

public static void show(int... arr)

{

      

}

可变参数其实就是上一种数组参数的简写形式,不用手动的建立数组对象,只要将要操作的元素作为参数传递即可。隐式将这些参数封装成了数组。

在使用可变参数新特性的时候注意:

可变参数一定要定义在参数列表的最后面。

 

静态导入

import static java.util.Arrays.*;//导入的Arrays这个类中的所有静态成员。

import static java.lang.System.*;//导入System类中所有的静态成员。

当类名重名时,需要指定具体的类名;

当方法重名时,指定具备所属的对象或者类。

 

 

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