您的位置:首页 > 职场人生

黑马程序员--Java基础--集合和集合中的泛型

2015-11-12 17:49 537 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

集合的概述

为了对基本数据类型的包装类方便操作基本数据类型值,将其封装为对象,在对象中定义了属性和行为丰富了数据的操作,用于描述该对象的类就称为基本数据类型对象封装类.

使用API的时候,要常查阅_API文档:当我们查阅了Api,发现Collection和collections.

collections作为工具类,里面都是静态方法.专门对集合尽兴操作的工具类.sort()方法,对List容器进行排序,不能对Set排序等等方法.

Collection是一个集合的根接口,集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象.对不同的类进行抽取共性,产生体系.叫做集合框架. 叫做collection(接口).



为什么会出现这么多的容器呢?

因为每一个容器对数据的存储方式都有所不同,这个存储方式:数据结构



常用对象已经被加黑标记!

要理解迭代器,可以借助抓娃娃,夹子在容器内部,访问内部的数据.

Collection

List 注意:List集合都有角标!!!

判断元素是否相同依据的是元素的equals()方法

ArrayList 数组数据结构 查询修改速度快 增删慢 非同步 默认长度是10 自动延长50% 申请一个新的150%的空间 拷贝

LinkedList 链表数据结构 查询速度确实有点慢 增删快

getFirst() getLast() 获取元素 peekfirstlast

removeFirst() removeLast()获取并删除元素 pollfirstlast

addFisrst() addLast() 头插尾插 offerfirstlast

Vector 底层是数组数据结构 查询没AL快 增删比LL慢.同步 默认长度10 自动延100%

Enumeration en = v.elements();

en.hasMoreElements()

en.nextElement();

元素是有序的 可以重复 因为该集合体系有索引.

凡是可以操作角标的方法

都是该体系特有的方法



add(index,ele)

addAll(index,Collection)



remove(index)



set(index,ele)



get(index)

subList(from,to)

ListIterator

Set 元素是无序的(存入和去除的数据不一定一致) 不可以重复

HashSet:底层数据是哈希表

HashSet是如何保证元素唯一性呢?

是通过元素的两个方法,hashCode()和equals来完成

如果元素的HashCode值相同,才会判断equals是否为真

如果元素的hashCode()值不同不会调用equals();

注意:对于判断元素是否存在.以及删除等操作,依赖的方法是hashCode();和equals();

对于ArrayList只调用equals();

TreeSet:底层数据是二叉树,可以对Set集合中的元素进行排序.

排序时当主要条件相同时一定要判断次要条件.

排序的第一种方式: 让元素自身具备比较性,即实现comparable接口覆盖int compareTo()

也称为元素的自然顺序,默认顺序.

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

在集合初始化时就有了比较方法.即集合的构造函数.

定义一个比较器,实现Comparator接口,覆盖compare方法.传递给TreeSet构造函数以使用比较器.

Collection:

1. List:元素是有序的,元素可以重复,因为该集合体系有索引.
2. Set:元素是无序的,元素不可以重复,该集合没有索引.


List集合可以在指定位置可以插入一个元素或者一对元素,可以获取指定位置元素获取元素位置删除元素等等.list可以改变一个位置上的元素,可以求子串subList()包含头不包含尾部.

凡是可以操作角标的方法都是该体系特有的方法.增删改查:List集合可以对元素进行增删改查,可以通过for循环取出元素,迭代器迭代过程中,不要是用集合方法操作元素.否则会造成ConcurrentModificationException.可以使用List Iterator(Iterator的子接口)来完成在迭代中对元素更多的操作.if(Obj.equals(“abc”)) it.add(“123”);这个语句的作用是在abc后面加一个元素”123”.

List集合中常见的三个子类对象:

ArrayList

查询极快,但增删稍微慢.

底层数据结构是用数组结构,特点在于查询速度很快.;线程不同步,一般使用此种办法,效率比Vector高.

Linklist

底层使用的是链表数据结构,特点在于增删快,查询很慢

Vector

底层是数组数据结构,跟ArrayList(JDK1.2)一模一样,Vector是JDK1.0出现.Vector是同步的因此效率低,被ArrayList替代.

什么叫可变长度的数组呢?

默认情况下ArrayList长度是10,超过时每次添加新元素,都延长现有的50%,把原数组的元素都copy到这个新的数组里面.Vector长度10,每次延长100%.

下面我们举例说明各个类以及类中方法的使用:

ArrayList

package collectionsDemo;

import java.util.Arrays;
import java.util.List;

public class ArrayDemo1 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
String [] arr = {"abc","bcd","def"};
List<String> al = Arrays.asList(arr);
System.out.println(al);

int [] nums = {2,4,5};
List<int[]> l = Arrays.asList(nums);

System.out.println(l);
}

}


//asList():将数组变成list集合.有什么用处呢? 可以使用集合的思想和方法来操作数组中的元素.

//将数组变成集合,不可以使用集合的增删方法,因为数组的长度是固定的,只要别改变增删就可以.

//增删 会报UnsupportedOperationException.

//如果数组中的元素都是对象那么变成集合的时候数组的元素都会变成集合中的元素.

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

package collectionsDemo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class ArrayDemo2 {

public st
4000
atic void main(String[] args) {
// TODO 自动生成的方法存根
ArrayList<String> al = new ArrayList<>();

al.add("abc1");
al.add("abc2");
al.add("abc3");

System.out.println(Arrays.toString(al.toArray(new String[al.size()])));
System.out.println(al);
for(String s : al)
{
s="lkk";
System.out.println(s);
}
}

}


//指定类型的数组要定义多长呢?当指定类型的数组长度小于了集合的size,

//该方法内部会创建一个新的数组,长度为集合的size().当指定类型的数组长度大于了集合的size()就不会

//新创建数组而是使用传递进来的数组.

//为什么要将集合变成数组?

//为了限定对元素的操作.安全的考虑.

package collectionsDemo;

import java.util.ArrayList;
import java.util.ListIterator;

public class ArrayListTest1 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
ArrayList a = new ArrayList();
a.add("java01");
a.add("java01");
a.add("java02");
a.add("java02");
a.add("java03");

System.out.println(a);

System.out.println(singleElement(a));
}

public static ArrayList singleElement(ArrayList a)
{
ArrayList al = new ArrayList();
for(ListIterator li = a.listIterator();li.hasNext();)  //ListIterator是List集合的专用迭代器,可以在迭代的过程中增删修改元素.而Collection接口中的Iterator则不能做这些操作!
{
Object obj = li.next();
if(!(al.contains(obj)))
al.add(obj);
}
return al;
}

}


什么是迭代器呢?

其实就是集合取出元素的方式.

对于取出这个动作,每个容器都有相应的存取操作,这个操作可能都可能有所不同,因为底层数据结构不同,所以实现方式都不同.这个取出方式不足以用一个方法来描述,需要用多个功能来体现.取出这个类就定义在了集合的内部.集合中存放着元素,想要取出元素,在该类内部定义一个类就可以方便的取出元素.每次取出元素之前都需要判断元素是否存在.把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类,(比如抓娃娃)而每一个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容:判断和取出,所以将共性抽取.

这就是Iterator().如何获取集合的取出对象呢?通过对外提供的方法Iterator().

高级for循环:for(数据类型 变量名:遍历的容器)这时不可以对集合进行操作只能读取.

迭代器除了遍历还可以进行remove()操作对集合中的元素进行删除.

如果是用list Iterator()还可以在遍历过程中尽兴增删改查的动作,但是该迭代器只适用于List集合!

高级的for循环于普通的for循环相比:高级for循环有一个局限性,必须有被遍历的目标,例如将一个字符串输出数遍,高级for就显得无能为力.建议在遍历数组的时候使用传统for,因为传统for可以操作数组的角标.

ListIterator是Iterator的子接口,在迭代时不可以通过集合对象的方法操作集合中的元素,因为会发生并发操作异常ConcurrentModificationException,所以只能用迭代器的操作,可是Iterator方法有限,如想要添加或修改等,就需要使用其子接口ListIterator.add是在当前对象后面加,set是直接修改当前对象

因为List带角标可以增删改查,所以ListIterator也可以.

该接口只能通过List集合的ListIterator方法获取.

Vector为什么被替代了但是还要提起?

枚举就是Vector的特有取出方式.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());
}


LinkedList:

里面有一些特有方法.

LinkedList 链表数据结构 查询速度确实有点慢 增删快

getFirst() getLast() 获取元素 peekfirstlast

removeFirst() removeLast()获取并删除元素 pollfirst();polllast();

addFisrst() addLast() 头插尾插 offerfirst();offerlast();

练习:

将自定义对象作为元素存ArrayList集合中,并去除重复元素.例如存人的对象,姓名性别年龄相同则视为重复.

思路:

1. 将数据封装进人的对象
2. 定义容器,将人存入
3. 筛选后取出


package collectionsDemo;

import java.util.ArrayList;
import java.util.ListIterator;

public class ArrayListTest2 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
ArrayList al = new ArrayList();
al.add(new Person2("lisi01",30));
al.add(new Person2("lisi01",30));
al.add(new Person2("lisi02",31));
al.add(new Person2("lisi03",32));

al = singleElement(al);
for(ListIterator li = al.listIterator();li.hasNext();)
{
Person2 p = (Person2)li.next();
System.out.println(p.getName()+"..."+p.getAge());
}

}

public static ArrayList singleElement(ArrayList a)
{
ArrayList al = new ArrayList();
for(ListIterator li = a.listIterator();li.hasNext();)
{
Object obj = li.next();
if(!(al.contains(obj))) //该方法调用的就是equals方法.
al.add(obj);
}
return al;
}
}
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 boolean equals(Object obj)
{
if(!(obj instanceof Person2))
return false;
Person2 p = (Person2)obj;
/*      if (this.name == p.name)
{
if(this.age == p.age)
return true;
return false;
}
return false;   */
//      System.out.println(this.name+"..."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
}

/*
* 往里面存的时候,add方法存为object,即为Object obj = new person("");Object中没有方法.Object obj = It.next();或者Person p = (Person)it.next();
在ArrayList中判断对象是否相同用equals方法.但是object类中比较的是地址值,复写equals方法即可.

*/


List集合判断元素是否相同依据的是元素的equals方法!其他集合是不同的.

设计到增删和查询的话,最好使用ArrayList,因为增删没有查询频繁.

Set

Set集合和List集合的区别:

无序:存入和取出的顺序不一定一致,元素不可以有重复.

Set集合的功能和collection是一致的.

常见子类:

HashSet

底层数据结构是哈希表.位置一样,同一个对象.

Set方法跟List方法都大致相同.

public class CollectionDemo5 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
TreeSet ts = new TreeSet();

ts.add("abcde");
ts.add("aaa");
ts.add("kkk");
ts.add("ccc");

Iteratorit = ts.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}

}
//取出的时候,顺序与存入是不同的,所以相对来说就是无序.但是由于TreeSet的特性,元素会在比较性上有序,这点我们在后来会讲到.


那么HashSet是如何保证元素唯一性呢?

是通过元素的两个方法,hashCode和equals方法.

如果HashCode值相同,才会判断equals是否为真,如果不同不会调用.

package collectionsDemo;

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo1 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
HsDemo h1 = new HsDemo(1,"啊啊");
HashSet hs = new HashSet();

hs.add(new HsDemo(11,"zhangsan"));
hs.add(new HsDemo(12,"zhangsan"));
hs.add(new HsDemo(12,"zhangsan"));

hs.add(h1);
hs.add(h1);
System.out.println(hs.contains(new HsDemo(11,"zhangsan")));;
System.out.println(hs.remove(new HsDemo(11,"zhangsan")));;
Iterator it = hs.iterator();
while(it.hasNext())
{
//              HsDemo hh = (HsDemo)it.next();
//              hh.getInfo();
((HsDemo)it.next()).getInfo();
}
}

}
class HsDemo
{
private int age;
private String name;
HsDemo(int age,String name)
{
this.age = age;
this.name = name;
}
public boolean equals(Object obj)
{
if(!(obj instanceof HsDemo))
return false;
HsDemo hs = (HsDemo)obj;

d3cd
return this.name.equals(hs.name)&&this.age==hs.age;
}
public int hashCode()
{
return name.hashCode()+this.age*13;//尽量保证返回值唯一.
}
public void getInfo()
{
System.out.println(this.name+":::"+this.age);
}
}


TreeSet在前面我们提到了TreeSet,存的时候顺序无需是有序的,取出时就有序.可以对Set集合中的元素进行排序.按照(ASCII码顺序).记住,排序时主要条件相同时,一定要判断次要条件.否则主要条件相同就存不进去.TreeSet:可以对Set集合中的元素进行排序,底层数据结构是二叉树,保证元素唯一性的依据是:compareTo方法,return int.

TreeSet排序第一种方式:让元素自身具备比较性,元素需要实现Comparable接口覆盖其中的compare方法.这种方式也成为元素的自然顺心,或者叫做默认顺序.

第二种排序方式:

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

在集合初始化的时候就有了比较方式,也就是修改其构造函数.

定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数.

package collectionsDemo;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo2 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
TreeSet ts = new TreeSet(new MyComparator());

ts.add(new Studenta("lisi01", 22));
ts.add(new Studenta("lisi02", 23));
ts.add(new Studenta("lisi04", 19));
ts.add(new Studenta("lisi004", 19));
ts.add(new Studenta("lisi0004", 19));
ts.add(new Studenta("lisi03", 24));
ts.add(new Studenta("lisi03", 22));

Iterator it = ts.iterator();
while (it.hasNext()) {
Studenta s = (Studenta) it.next();
System.out.println(s.getAge() + "::::" + s.getName());
}
}

}

class MyComparator implements Comparator { //定义一个比较器
public int compare(Object obj1, Object obj2) {
if (!((obj1 instanceof Studenta) && (obj2 instanceof Studenta)))
throw new RuntimeException("对象异常!");
Studenta s1 = (Studenta) obj1;
Studenta s2 = (Studenta) obj2;
int num = s1.getName().compareTo(s2.getName());

if(num==0)
{
/*if(s1.getAge()>s2.getAge())
return 1;
if(s1.getAge()<s2.getAge())
return -1;
return 0;*/
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
}

return num;

}
}

class Studenta implements Comparable {
private String name;
private int age;

public int compareTo(Object obj) {
if (!(obj instanceof Studenta))
throw new RuntimeException("对象异常!");
Studenta s = (Studenta) obj;
if (this.age > s.age)
return 1;
if (this.age < s.age)
return -1;
return this.name.compareTo(s.name);
// return 1;//这样取出的时候按原顺序取出
}

Studenta(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}
/*
当两种排序都存在的时候以比较器为主,Comparator. 定义一个类实现comparator接口覆盖其compare方法.return int.
往TreeSet里面存字符串,字符串本身具备比较性,但是它的比较方法不一定是我们所需要的,这时候只能是用比较器.
搞一个类实现Comparator接口,覆盖其Compare方法,主要条件筛选完要筛选次要条件,否则会造成元素丢失!

*/


泛型:

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

好处:

1.将运行时期出现的问题ClassCastException转移到了编译时期,方便程序员解决问题,运行时的问题在编译的时候就被解决,安全.

2.避免了强制转换的麻烦.

泛型格式:

通过<>来定义要操作的引用数据类型.

在使用java提供的对象的时候,什么时候写泛型呢?

通常在集合框架中很常见.只要见到<>就要定义泛型.

其实<>就是用来接收类型的.

当使用集合的时候,将集合中药存储的数据类型作为参数传递到<>中即可.

Utilss<Worker> u = new Utilss();
u.setObject(new Worker());
Worker w = (Worker)u.getObject();


class Utilss

{

private QQ q;

public void setObject(QQ q)

{

this.q = q;

}

public QQ getObject()

{

return q;

}

}

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

泛型类定义的泛型,在整个类中有效.如果被方法使用,那么泛型类的对象明确要操作的具体类型以后,所有的要操作的类型就已经固定了.

为了让不同方法操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上.

//静态方法不可以访问类上定义的泛型!
//如果静态方法操作的引用数据类型不确定可以将泛型定义在方法上.
public static void print(ArrayList<?> al)
{
Iterator<?> it = al.iterator();
while(it.hasNext())
System.out.println(it.next());
}


不可以使用类型中特有的方法.因为是???

? 叫通配符,也可以理解为占位符.编译时无法明确的类型.

泛型的限定:

?extends E :可以接受E类型或者E的子类类型.向下限定.

?super E:可以接受E类型或E类型的父类类型.向上限定.

Map集合

接口
MAP<K,V>
//key,value

Map集合:首先该集合存储键值对,而且要保证键的唯一性.

Collection装的是单独的数据,是单列的集合.

而Map是双列集合.

Map集合中:

添加

Put(K,V)

删除

Clear()

Remove(Object key)

判断

ContainsValue(Object value)

Contains(Object key)

isEmpty()

获取

Get(Object key)

Size()

Values()

EntrySet()

keySet()

Map的子类:

|–HashTable

底层是哈希表数据结构,不可以存入null键或值.该集合是线程同步的,必须实现hashCode方法和equals方法

JDK1.0出现

|–HashMap

底层基于哈希表数据结构,允许使用NULL作为键值.该集合是线程不同步的.JDK1.2出现,效率较HashTable高.

|–TreeMap

底层是二叉树数据结构,线程不同步,可以用于给MAP集合中的键进行排序.

Map和Set很像,其实Set底层就是使用Map.

Map

//第1种取出方式:keySet
Set<String>  keySet = map.keySet();//有了Set集合就可以获取其迭代器,
Iterator it = keySet.Iterator();
While(it.hasNext)
{
String key = it.next();//有了键就可以通过map集合的get方法获取其对应的值.
String value = map.get(key);
Sop(key);
}


Map集合的取出原理:将map集合转成Set集合,通过迭代器获取取出.

Set<> entrySet:
Set<Map.Entry<K,V>> entrySet.//将map集合中的映射关系存入到Set当中,而这个关系的数据结构就是Map.Entry.将Map集合中的映射关系取出,存入到Set集合中,
//第2种取出方式:entrySet
Set<Map.entry<String,String>> entrySet = map.entrySet();
Iterator<Map.entry<String,String>> it = entrySet.iterator();
While(it.hadNext()){
Map.Entry<String,String> me = it.next();
String key= me.getKey();
String value= me.getValue();
}


Map.Entry 其实Entry也是一个接口,是Map接口中的一个内部接口.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java