JAVA基础(三十四)集合

【集合的由来】
    对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。
就使用集合容器进行存储

【集合框架的构成及分类】

1、集合的顶层

 Collection
             |--List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
      |--Set:元素不能重复,无序。
  
        Map
      |
      |--HashMap
      |--TreeMap

2、list的子类

List:list
    |--Vector:内部是数组数据结构,是同步的。增删,查询都很慢!
    |--ArrayList:内部是数组数据结构,是不同步的。替代了Vector。查询的速度快。
    |--LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。


3、Set的子类
Set:元素不可以重复,是无序。
    Set接口中的方法和Collection一致。
    |--HashSet: 内部数据结构是哈希表 ,是不同步的。
        如何保证该集合的元素唯一性呢?
        是通过对象的hashCode和equals方法来完成对象唯一性的。
        如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。 
        如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
        如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。
        
        记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。
        一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。
        建立对象判断是否相同的依据。


* 练习:
 * "fdgavcbsacdfs" 获取该字符串中,每一个字母出现的次数。
 * 要求打印结果是:a(2)b(1)...;

public static String getCharCount(String str) {
        
        
        //将字符串变成字符数组 
        char[] chs = str.toCharArray();
        
        //定义map集合表。
        Map<Character,Integer> map = new TreeMap<Character,Integer>();
        
        for (int i = 0; i < chs.length; i++) {
            
            if(!(chs[i]>='a' && chs[i]<='z' || chs[i]>='A' && chs[i]<='Z'))
//            if(!(Character.toLowerCase(chs[i])>='a' && Character.toLowerCase(chs[i])<='z'))
                continue;
            
            //将数组中的字母作为键去查map表。            
            Integer value = map.get(chs[i]);
            
            int count = 1;
            
            //判断值是否为null.
            if(value!=null){
                count = value+1;
            }
//            count++;
            map.put(chs[i], count);
            /*
            if(value==null){
                map.put(chs[i], 1);
            }else{
                map.put(chs[i], value+1);
            }
            */
        }
        
        
        
        
        
        return mapToString(map);
    }

    private static String mapToString(Map<Character, Integer> map) {
        
        StringBuilder sb = new StringBuilder();
        
        Iterator<Character> it = map.keySet().iterator();
        
        while(it.hasNext()){
            Character key = it.next();
            Integer value = map.get(key);
            
            sb.append(key+"("+value+")");
        }
        
        return sb.toString();
    }


        
        
        
        
    |--TreeSet:可以对Set集合中的元素进行排序。是不同步的。 
                判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。 
                
                TreeSet对元素进行排序的方式一:
                让元素自身具备比较功能,元就需要实现Comparable接口。覆盖compareTo方法。
                
                如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?
                可以使用TreeSet集合第二种排序方式二:
                让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。
                将该类对象作为参数传递给TreeSet集合的构造函数。
                
4、Map常用的子类:
    |--Hashtable :内部结构是哈希表,是同步的。不允许null作为键,null作为值。
        |--Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合。         
    |--HashMap : 内部结构是哈希表,不是同步的。允许null作为键,null作为值。
    |--TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。 
    


【集合特点】
1.用于存储对象的容器
2.集合的长度是可变的
3.集合中不可以存储基本数据类型

【集合的共性】
集合容器因为内部的数据结构不同,有多种具体容器


不断向上抽取,就形成了集合框架

框架的顶层Collection的常见方法:

1.添加

boolean add(Object obj);

boolean addAll(Conlection<?> coll);
2.删除

boolean remove(Object obj);

boolean removeAll(Collection coll);//将两个集合中的相同元素从调用removeAll的集合中删除

clear();

3.判断

boolean contains(Object obj)

boolean containsAll(Collection obj);//如果此集合包含指定collection中所有元素,则返回true;

boolean isEmpty();

4.获取

size();


iterator();取出元素的方式:迭代器


5.其他
retainAll();取交集,删除两个集合不相同的元素,和removeAll相反

toArray();将集合转成数组

【迭代器】
相关方法
hashNext();//若果仍有元素可以迭代,则返回true;
next();//首先返回第一个元素,然后他会自动往后走
remove();移除迭代器返回最后一个元素

使用:

Collection coll=new ArrayList();
coll.add("abc");
Iterator it=coll.iterator();
while(it.hasNext){
System.out.println(it.next());

}
这个方式最后it不会被释放,暂用内存

开发中用

for(Iterator it=coll.iterator();it.hasNext;){
System.out.println(it.hasNext());
}

【迭代原理】

该对象必须依赖于具体容器(集合),因为每一个容器的数据结构都不同
所以该迭代器对象是在容器中进行内部实现的


迭代器可以实现迭代过程中完成对元素的增删改查

ListIterator it = list.listIterator();
while(it.hasNext){
  Object obj=it.next();
  if(obj.equals("")){
    it.set();//修改元素

   //只有List集合具备这个迭代功能

}


}

【list与set的特点】

Collection是一个接口


常用的两个这个接口下的子体系

List和set连个接口

【List】

    有序(存入和取出的顺序一致)元素都有索引(角标),元素可以重复
常用方法:
List list=new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc3");
1.添加元素
void add(Object obj);

2.插入元素
void add(index,element);
void add(index,collection);

3.删除元素
Object remove(index);

4.修改元素
Object set(index,element);

list.set(0,"bcd");//输出list为bcd

5.获取
Object get(index);
int indexOf(Object);
int lastIndex();
List suList(from,to);//包含from,不包含to

list.subList(1,2);//输出abc1

listIterator:列表迭代器,只有list有
在迭代器过程中,不要使用集合操作元素,容易出现异常。
while(it.hasNext()){
  Object obj=it.next();
  if(obj.equals("abc2")){
    list.add("abc2");

}eles{
system.out.println("next:"+obj);
}
}
}

会报ConcurrentModificationException,因为iterator初始化的时候只有3个,
这个时候往集合中添加元素,下一次循环的时候就会产生冲突
解决:
使用Iterator接口的子接口listIterator来完成在迭代中对元素进行更多的操作
方法:
listIterator(int index);//列表迭代器从指定位置开始
add(E e);//将指定的元素加入列表
hasNext()   
hasPrevious();//如果以逆向遍历列表,列表迭代器有多个元素,则返回true
next();
nextIndex();//返回next的后续调用所返回元素的索引
previousIndex();//返回对previours的后续调用返回元素的索引
remove();
set(E e);//用指定元素替换next或previous返回的最后一个元素


【list集合的子类】

Vector 

内部是数组数据结构。是同步的。几乎不用


ArrayList

内部是数组数据结构,是不同步的。替代了Vector


ArrayList可增长的原理


在内存中在new一个新的数组(这个数组比原来的数组更大),然后把原来的数组复制到这个新数组中

特点:增删的速度不快,查询速度快。查询块是因为对象存在相连的一片连续的空间里面,而LinkeList则不是在连续的空间中


LinkedList   链接列表


结构:链表1记录的是链表2的地址

如:
(链表1 记录98)--(链表2 地址98  记录22)---(链表3  地址22。。。)

删除元素2时,只需让链表1  记录22,链表2就被删除了


内部是链表数据结构,是不同步的。

特点:操作元素的增删元素速度快,查询相对ArrayList慢。


LinkedList也有角标


【LinkeList】

addFirst();添加到头部

getFirst();获取头部

removeFirst();删除头部


模仿堆栈和队列

//先进先出
public class DuiLie{

private LinkedList link;

public (){

    link=new LinkedList();    


}


public void myAdd(Object obj){

    link.addLast(obj);

}


public Object myGet(){


    return link.removeFirst();

}


}


如果改成栈 就反过来就可以了


【set】

    元素不能重复,无序,Set接口中的方法和Collection一致。

如:HashSet hs=new HashSet();

hs.add("1");
hs.add("2");
hs.add("1");
hs.add("3");
hs.add("4");

set集合的遍历必须使用iterator

无序不重复输出:4,2,1,3


【set的子类】

HashSet

内部数据结构是哈希表 ,是不同步的。


哈希表:
哈希是一种算法,这种算法会算出来很多值,这些值存储起来就是哈希表,
这个表有对应关系。这种算法对数组存储有优化,查找速度很快。

哈希表判断元素是否重复的方法:1.判断两个元素的哈希值是否相同,如果相同再判断两个对象的内容是否相同。

2.判断哈希值相同,其实判断的是对象的hashCode的方法。判断内容相同用的是equals方法

注意:如果哈希值不同,是不需要判断equals的。   


如  "ba"和"ba",就需要判断两次


自定义对象如果需要在set中保证唯一性需要重写hashcode方法和equals方法。

如:

class Person {

    private String name;
    private int    age;

    public Person(String name,int age){
        this.name=name;
        this.age=age;
`
}

    @Override
    public int hashCode() {
//        System.out.println(this+".......hashCode");
        
        return name.hashCode()+age*27;//乘以随便一个数保证hashcode的唯一性

    }
    @Override
    public boolean equals(Object obj) {
        
        if(this == obj)
            return true;
        if(!(obj instanceof Person))
            throw new ClassCastException("类型错误");
        
//        System.out.println(this+"....equals....."+obj);
        Person p = (Person)obj;
        
        
        
        return this.name.equals(p.name) && this.age == p.age;
    }

}

HashSet的子类:  LinkedHashSet  有序并保证唯一性
  


treeSet


可以对Set集合中的元素进行排序。是不同步的。

判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。 

TreeSet 存储元素的有序排序的方式是二叉树,元素跟已存在的元素进行比较,大的在右边,小的在左边
                
                TreeSet对元素进行排序的方式一:
                让元素自身具备比较功能,元素就需要实现Comparable接口。覆盖compareTo方法。


public class Person /*extends Object*/ implements Comparable {

    private String name;
    private int age;
    
    
    public Person() {
        super();
        
    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    
    
    
    @Override
    public int hashCode() {
//        System.out.println(this+".......hashCode");
        
        return name.hashCode()+age*27;
//        return 100;
    }
    @Override
    public boolean equals(Object obj) {
        
        if(this == obj)
            return true;
        if(!(obj instanceof Person))
            throw new ClassCastException("类型错误");
        
//        System.out.println(this+"....equals....."+obj);
        Person p = (Person)obj;
        
        
        
        return this.name.equals(p.name) && this.age == p.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 name+":"+age;
    }
    @Override
    public int compareTo(Object o) {
        
        Person p = (Person)o;
        
        int  temp = this.age-p.age;
        return temp==0?this.name.compareTo(p.name):temp;
        
//        int  temp = this.name.compareTo(p.name);
//        return temp==0?this.age-p.age:temp;
        
        /*
        if(this.age>p.age)
            return 1;
        if(this.age<p.age)
            return -1;    
        
        else{
            
            return this.name.compareTo(p.name);
        }
        */
        
    }
    
}


                
                如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?
                可以使用TreeSet集合第二种排序方式二:
                让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。
                将该类对象作为参数传递给TreeSet集合的构造函数。(这个方式比方式一优先级更高)


public class ComparatorByName implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {

        Person p1 = (Person)o1;
        Person p2 = (Person)o2;
        
        int temp = p1.getName().compareTo(p2.getName());
        
        return temp==0?p1.getAge()-p2.getAge(): temp;

    }

}

使用:TreeSet ts = new TreeSet(new ComparatorByName ());


特殊用法:如果需要TreeSet有序输出,只需要把compare固定返回1(正序)或者-1(倒序)


字符串按照长度排序,如果长度一样再按照字母排序

public class ComparatorByLength implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {

        Person p1 = (String)o1;
        Person p2 = (String)o2;
        
        int temp = p1.length().compareTo(p2.length());
        
        return temp==0?p1.comparaTo(p2): temp;

    }

}


只有List有

  list.get(i);方法,set是没有的

【Map】

常用方法:
1,添加。
    value put(key,value):返回前一个和key关联的值,如果没有返回null.

2,删除。
    void  clear():清空map集合。
    value remove(key):根据指定的key翻出这个键值对。 

3,判断。
    boolean containsKey(key):
    boolean containsValue(value):
    boolean isEmpty();

4,获取。 
    value get(key):通过键获取值,如果没有该键返回null。
                    当然可以通过返回null,来判断是否包含指定键。 
    int size(): 获取键值对的个数。


遍历map集合

第一个方法

原理通过keySet获取所有map中所有的键所在的Set集合,通过set的迭代器获取到每一个键,再对每一个键通过Map集合的get方法取其对应的值即可


Set<Object>set=map.ketSet();
Iterator<Object>it=set.iterator();
while(it.hashNex()){

    Object key=it.next();
    Object value= map.get(key);

}


第二种方法:

先把map转成Set集合


找到另一个方法,entrySet

该方法将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.Entry类型


Set<Map.Entry<Object,Object>>entrySet=map.entrySet();
Iterator<Map.Entry<Object,Object>>it =entrySet.iterator();
while(it.hasNext()){
    Map.Entry<Object,Object>me=it.next();

    Object key = me.getKey();
    Object value =me.getValue();
        

}


【获取所有值】

values();

Collection <Object>  values=map.values();

Iterator <Object> it=values.iterator();

while(it.hashNext()){
    Object value=it.next();
}

Map常用的子类:
    |--Hashtable :内部结构是哈希表,是同步的。不允许null作为键,null作为值。
        |--Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合。         
    |--HashMap : 内部结构是哈希表,不是同步的。允许null作为键,null作为值。
    |--TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。 

    
【泛型】    
    
1.由来

    jdk1.5出现的安全机制。

    泛型出现之前:

    ArrayList a1= new ArrayList();

    a1.add("abc");
    a1.add("hhah");
    a1.add(4);
    Iterator it=a1.iterator();
    while(it.hasNext()){
        String str=(String)it.next();
    }

这个时候就出现了安全隐患,4不能转成String,所以就需要在定义的时候明确类型,

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


左边是声明,右边是实体,这样操作的类型就被指定了,这就是泛型。


2.好处:

    将运行时期的问题ClassCastException转到落编译时期
    避免了强制转换的麻烦

<>:什么时候用?当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型传入即可.
   其实<>就是一个用于接收具体引用数据类型的参数范围。

在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型 。


比如说ArrayList<E>,E接收的数据类型是引用数据类型,而引用数据类型不是接口就是类,这就是为什么不用e。


泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全。

运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?因为为了兼容运行的类加载器。

泛型的补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。


泛型的通配符:? 未知类型。 

泛型的限定:
? extends E: 接收E类型或者E的子类型对象。上限
一般存储对象的时候用。比如 添加元素 addAll.

? super E: 接收E类型或者E的父类型对象。 下限。
一般取出对象的时候用。比如比较器。


我们这一代的程序员在使用已经定义了泛型的JDK API时必须明确传入的数据类型,为什么有时候不传也可以呢,那是为了兼容老版本,但是会有警告。


泛型方法:

定义一个工具类,

public class Tools<T>{


public void show(T t){

    System.out.println("show:"+t);

}


}

使用:

Tool<String>tool=new Tool<String>();

上面已经明确了泛型的类型是String,
tool.show(new Integer(4));这样子就会报错

如果想让方法操作的类型不确定,需要把泛型定义在方法上

public <W> void show(W w){

    System.out.println("show:"+w);

}

当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,只能将泛型定义在方法上

注意格式,不要把泛型放在修饰符前面
public  static <W> void show(W w){

    System.out.println("show:"+w);

}

泛型接口,将泛型定义在接口上。

interface Inter<T>{

    public void show(T t);
}


class InterImp1 implements Inter<String>{

    public void show(String str);
}


使用: InterImpl in=new InterImpl();
    in.show("abc");

还有一种情况。他的实现类还是不知道具体的类型
class InterImp12<Q> implement Inter<Q>{
  
   Public void show(Q q);    

}

那就在使用的时候明确类型


InterImpl in2=new InterImpl();
in2.show(5);

泛型的限定


<?>通配符  可以接受所有 类型


<? extend T> 可以接受T和T的子类,这就是上限,一般存储元素的时候都是用上限,因为这样取出按照上限类型来运算的。不会出现安全隐患

<? super T>可以接收T和T的父类  这就是下限,通常对集合中的元素进行取出操作时,可以使用下限

===========================================================

集合的一些技巧:

需要唯一吗?
需要:Set
    需要制定顺序: 
            需要: TreeSet
            不需要:HashSet
            但是想要一个和存储一致的顺序(有序):LinkedHashSet
不需要:List
    需要频繁增删吗?
        需要:LinkedList
        不需要:ArrayList
        
如何记录每一个容器的结构和所属体系呢?

看名字!


List
    |--ArrayList
    |--LinkedList

Set
    |--HashSet
    |--TreeSet

后缀名就是该集合所属的体系。

前缀名就是该集合的数据结构。

看到array:就要想到数组,就要想到查询快,有角标.    
看到link:就要想到链表,就要想到增删快,就要想要 add get remove+frist last的方法 
看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法。 
看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator 。

而且通常这些常用的集合容器都是不同步的。 


============================================

map练习1:

"fdgavcbsacdfs" 获取该字符串中,每一个字母出现的次数。


    public static String getCharCount(String str) {
        
        
        //将字符串变成字符数组 
        char[] chs = str.toCharArray();
        
        //定义map集合表。
        Map<Character,Integer> map = new TreeMap<Character,Integer>();
        
        for (int i = 0; i < chs.length; i++) {
            
            if(!(chs[i]>='a' && chs[i]<='z' || chs[i]>='A' && chs[i]<='Z'))
//            if(!(Character.toLowerCase(chs[i])>='a' && Character.toLowerCase(chs[i])<='z'))
                continue;
            
            //将数组中的字母作为键去查map表。            
            Integer value = map.get(chs[i]);
            
            int count = 1;
            
            //判断值是否为null.
            if(value!=null){
                count = value+1;
            }
//            count++;
            map.put(chs[i], count);
            /*
            if(value==null){
                map.put(chs[i], 1);
            }else{
                map.put(chs[i], value+1);
            }
            */
        }
        
        
        
        
        
        return mapToString(map);
    }

    private static String mapToString(Map<Character, Integer> map) {
        
        StringBuilder sb = new StringBuilder();
        
        Iterator<Character> it = map.keySet().iterator();
        
        while(it.hasNext()){
            Character key = it.next();
            Integer value = map.get(key);
            
            sb.append(key+"("+value+")");
        }
        
        return sb.toString();
    }

map练习2


查表法


    public static String getWeekByMap(String week){
        
        Map<String,String> map = new HashMap<String,String>();
        
        map.put("星期一","Mon");
        map.put("星期二","Tus");
        map.put("星期三","Wes");
        map.put("星期日","Sun");
        map.put("星期天","Sun");
        
        return map.get(week);
    }
    
    
    public static String getWeek(int week){
        
        if(week<1 || week>7)
            throw new RuntimeException("没有对应的星期,请您重新输入");
        
        String[] weeks = {"","星期一","星期二"};
        
        return weeks[week];
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值