集合

集合

1.0 集合特点:
1.1 Object
1.2 集合框架的工具类
1.3 泛型
1.4 集合使用技巧
1.5 foreach
集合练习

    下边的挨个介绍一下吧:


集合

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

1.0 集合特点:

存储对象,长度可变.可进行增删改查

1.1 Object  

|----- Collection接口

1.方法:
添加
boolean    add(Object obj);
boolean    addAll(Collection coll)
删除
boolean    remove(Object obj);  集合的remove会改变集合的长度。
boolean    removeAll(Collection coll); 将两个集合中的相同元素从调用removeAll的集合中删除。
void  clear();
判断
boolean     contains(Object obj);
boolwan    containsAll(Collection coll); 是否包含另外一个集合里面所有的元素。
boolean isEmpty();判断集合中是否有元素。
获取
int   size();
Iterator iterator();取出元素的方式:迭代器
迭代器: 对象必须依赖于具体的容器,因为每个容器的数据结构都不同。所以该迭代器对象是在容器中进行内部实现的。
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法。
Iterator接口就是对所有的Collection容器进行元素去除的公共接口。
使用Collcetion中的iterator()方法。调用集合中的迭代器方法,是为了获取集合中的迭代器对象。
其他
boolean     retainAll(Collection coll); 取交集。与removeAll相反,保留和指定集合相同的元素,而删除不同的元素
Object [ ] toArray(); 将集合转成数组。
|-----------List

有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。具有增删改查
特有常见方法:有一个共性特点就是都可以操作角标。
添加
void     add( index,element)
void   add(index,collection);
删除
Object      remove(index);
修改
Object      set(index,element);
获取:
Object        get(index) //List特有的取出方式之一。
int      indexOf(object)
int      laseIndexOf(object)
List    subList(from,to)//包含头,不包含尾
List特有的取出方式代码:
      for(int x = 0;x<list.size();x++){
           System.out.println(list.get(x));
    }
List集合是可以完成对元素的增删改查。

|----------------Arraylist

内部是数据数据结构,是不同步的。替代了Vector。查找元素的速度很快
练习:

练习:用ArrayList删除里面重复的元素。
importjava.util.ArrayList;
importjava.util.Iterator;

public class ArrayListTest2 {
    public static void main(String[] args) {
       ArrayList a1 = newArrayList();
       a1.add("abc1");
       a1.add("abc2");
       a1.add("abc3");
       a1.add("abc4");
       a1.add("abc5");
       a1.add("abc2");
       a1.add("abc3");
       a1 = getSingleElement(a1);
       Iterator it = a1.iterator();
       while(it.hasNext()){
           System.out.println(it.next());
       }
    }

    public static ArrayList getSingleElement(ArrayList a1) {
       ArrayList temp = newArrayList();
       Iterator it = a1.iterator();
       while(it.hasNext()){
           Object obj = it.next();
           if(!temp.contains(obj)){
              temp.add(obj);
           }
       }
       return temp;
    }
}


练习二:需求:同姓名同年龄视为同一个人,视为重复元素。去除同一元素。
Person类中的equals方法继承Object,判断相同的依据是Person对象的地址值。
为了按照指定的条件来判断Person对象是否相同,必须覆盖equals方法。

public class Person {
    private Stringname;
    private int age;
    public Person() {
       super();
    }
    public Person(String name,int age) {
       super();
       this.name = name;
       this.age = age;
    }
    public String getName() {
       returnname;
    }
    @Override
    public boolean equals(Object obj) {//因为所有的类都继承了Objectequals方法,所以要进行向下转型
       //记住要加健壮性的判断,如果传的不是Person直接让程序挂掉
       if(!(objinstanceof Person))
           throw new ClassCastException("类型错误");
       Person p = (Person)obj;
       return this.name.equals(p.name) &&this.age==p.age;
    }
    public void setName(String name) {
       this.name = name;
    }
    public int getAge() {
       returnage;
    }
    public void setAge(int age) {
       this.age = age;
    }
    @Override
    public String toString() {//覆盖toString方法是为了让集合迭代器打印的不是对象的哈希值(地址值)。
       return"Person :"+name+" "+age;
    }   
}

importjava.util.ArrayList;
importjava.util.Iterator;

importbeen.Person;

public class ArrayListTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
       ArrayList a = newArrayList();
       a.add(new Person("lisi1",21));
       a.add(new Person("lisi2",22));
       a.add(new Person("lisi3",23));
       a.add(new Person("lisi4",24));
       a.add(new Person("lisi3",23));
       a.add(new Person("lisi4",24));

       a = getSingleElement(a);

       Iterator it = a.iterator();
       while(it.hasNext()){
           System.out.println(it.next());
       }
    }

    public static ArrayList getSingleElement(ArrayList a) {
       //定义一个临时的集合容器
       ArrayList temp = newArrayList();
       Iterator it = a.iterator();
       while(it.hasNext()){
           //取出的元素都是对象
           Object obj = it.next();
           //进行判断,如果临时的集合容器有这个元素对象,就不进行存储
           //没有的话就进行存储。
           if(!temp.contains(obj))
              temp.add(obj);
       }
       //返回这个临时集合容器
       return temp;
    }
}


注意:

ArrayList:当自定义对象进行取出时,一定要先进行向下转型。

ArrayList:判断里面元素是否相同,用的是对象所属的equals方法来判断是否包含。

remove和contains方法其实都是依赖于对象的equals方法来判断是否相同,相同即包含。

|----------------Linkedlist

内部是链表数据结构,是不同步的。增删元素的速度很快。
常见方法:


addFirst();
addLast();
JDK1.6
offerFirst();
offetLast();

getFirst();获取但不移除,如果链表为空,抛出NoSuchElementException.
getLast();
JDK1.6
peekFirst();获取但不移除,如果链表为空,返回null
peekLastt();

removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException
removeLast();
JDK1.6
pollFirst();//获取并移除,如果链表为空,返回null。
pollLast();
面试题:

请使用用LinkedList,来模拟一个对战或者队列数据结构。

堆栈:先进后出。 First In Last out FIFO

队列:先进先出。 First In First Out FIFO

应该描述这样一个容器,给使用提供一个容器对象完成者两种结构中的一种。
堆栈表现形式:
packageit.cast_01;

importjava.util.LinkedList;

public class DuiLie {
    private LinkedList link;
    public DuiLie(){
       link = newLinkedList();
    }
    public void myAdd(Object obj){
       link.add(obj);
    }
    public Object myGet(){
       returnlink.removeLast();//如果要改成队列,link.removeFirst()
    }
    public boolean isNull(){
       returnlink.isEmpty();
    }
}



packageit.cast_01;

public class DuiLieZhanDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
       DuiLie d = new DuiLie();
       d.myAdd("haha");
       d.myAdd("hehe");
       d.myAdd("xixi");
       d.myAdd("hiahia");
       while(!d.isNull()){
           System.out.println(d.myGet());
       }
    }
}



|----------------Vector

内部是数组数据结构,是同步的。增删,查询都很慢!
Enumeration接口
此接口的功能与Iterator接口的功能是重复的,Iterator接口添加了一个可选的移除操作,并使用较短的方法名。


扩展:Arraylists和linkedlist区别:

因为Arraylist是数组结构可以通过角标操作集合里面的元素,但是linkedlist是链表结构只能操作头和尾上的元素。但是有人会问?List集合不是有序的吗?此时的有序是想相对于存入
和取出是一致的。怎样放就怎样取,ArrayList是连续的空间,而linkedlist是不连续的空间。
依角标查询是依然一个一个的向下询问。


|-----------Set

元素不可以重复,是无序。
set集合的底层就是map集合
|----------------TreeSet

TreeSet:是二叉树结构其实就是二分查找,对已有的有序元素进行折半,再确定新元素的位置。二叉树其实就是看的返回值,对象比较的大小。
二叉树结构示意图:




如果要按照怎么存入就怎么取出元素,上图可以看出,比顶层大的元素都在右边,只要在比较器里面的compare里面返回1就行。二叉树具体是看对象比较的返回值。

示例代码:
importjava.util.Comparator;

public class ComparatorByName implementsComparator {
    /**
     * 创建了一个根据Person类的name进行排序的比较器
     */
    @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;*/
       return 1;
    }


|----------------hashSet



|-----Map集合

Map:一次添加一对元素。Collection一次添加一个元素。
Map也称为双列集合,Collection集合称为单列集合。
其实Map集合中存储的就是键值对。
Map集合中必须保证键的唯一性。
Map常用方法:
添加
value put(key,value); 返回前一个和key关联的值,如果没有返回null
删除
void  clear(); 清空map集合
value remove(key); 根据指定的key删除这个键值对
判断
boolean    containsKey(key);


boolean    containsVaule(value);


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


底层数据结构是哈希表,线程不同步,可以存入null键值。要保证键的唯一性,需要覆盖hashCode()方法和equals()方法
|----------LinkedHashMap



该子类基于哈希表又融入链表,可以使map集合增删时提高效率,
怎么存入就怎么取出元素



|---------Hashtable

底层结构是哈希表数据结构,线程是同步,不可以存入null键值对。效率较低,被hashmap锁替代

|-----------Properties
用来存储键值对型的配置文件的信息,可以和IO技术相结合


|---------Treemap


底层是二叉树结构,可以对map集合中的键进行排序。需要使用comparable或者comparator进行排序。

思考:
如果有映射关系应该最先想到数组,然后在思考这个映射关系是否是有序的,如果没有序的话就应该考虑到Map集合,因为Map集合存储的是键值对,映射关系是不需要有序的


1.2 集合框架的工具类


该类中的方法都是静态的。
|-------Arrays



List asList(数组)将数组转成集合


如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行集合存储。
如果数组中的元素是基本类型数值,那么会将该数组作为集合中的元素进行存储。


将数组转成集合!
            好处:可以使用集合中非方法操作数组中的元素
            注意:数组的长度是固定的,所以对于集合的增删方法是不可以使用的
            否则会发生UnsupportedOperationException   
            如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行集合存储
            如果数组中的元素是基本数据类型数值,那么就会将该数组作为集合中的元素进行存储





|-------Collections



reverseOrder()  获取一个比较器,用于反转集合中的内容  
reverse(List<?> list)  反转
fill(List,T obj)  将集合中的元素一次性全部替换
shuffle(list);   随机把这些元素安放在任意位置上

给非同步的集合加锁  synchronizedList(List list)


使用的就是Collection借口中的toArray方法。
集合转成数组:可以对集合中的元素操作的方法进行限定。不允许对其进行增删。
toArray方法需要传入一个指定类型的数组。
长度该如何定义呢?
如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同的size的数组。
如果长度大于集合的size,那么该方法就会使用指定的数组,存储集合中的元素,其他位置默认为null。
所以建议,最后长度就指定为集合的长度size()









1.3 泛型



泛型<>什么时候用:


当操作的引用数据类型不确定的时候,就使用<>泛型,将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。
将泛型定义在方法上:public <W> void show(W str)
当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,只能将泛型定义在方法上。如:public static<Y> void show(Y obj);
注意:泛型必须放在修饰符后面返回值的前面。

泛型接口,将泛型定义在接口上。
示例代码:
public class GenericDemo2 {

    /**
     * @param args
     */
    public static void main(String[] args) {
       Imple1 in1 = new Imple1();
       in1.show("haha");
       Imple2<Integer> in2 = new Imple2<Integer>();
       in2.show(new Integer(5));
    }

}
interfaceInter<T>{
    public void show(T t);
}

class Imple1 implements Inter<String>{
    public void show(String str){
       System.out.println("show  "+str);
    }
}
//如果实现接口也不知道传什么类型的话就自定义
classImple2<Q> implements Inter<Q>{
    public void show(Q q){
       System.out.println("show  "+q);
    }
}


泛型的擦除于补偿:(作为了解)

注意细节:
泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。
运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?因为为了兼容运行的类的加载器。

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



泛型的通配符:

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


示例代码:
importjava.util.ArrayList;
importjava.util.Collection;
importjava.util.HashSet;
importjava.util.Iterator;
public class GenericDemo3 {
    /**
     * @param args
     */
    public static void main(String[] args) {
       ArrayList<String> al1 = new ArrayList<String>();
       al1.add("haha");
       al1.add("xixi");
       HashSet<Integer> hs = new HashSet<Integer>();
       hs.add(new Integer(5));
       hs.add(new Integer(6));
      
       printCollection(al1);
       printCollection(hs);
    }
    public static void printCollection(Collection<?> al1) {
       //如果打印的集合多,直接用父类的接口大集合作为参数类型
       //如果打印的多个集合的类型不同,可以用通配符?的形式来表示
       Iterator<?> it = al1.iterator();
       while(it.hasNext()){
           System.out.println(it.next());
       }
    }
}



泛型类型限定:

可以对类型进行限定:? extends E :接收E类型或者E的子类型对象。叫上限。
                                               ? super E: 接收E类型或者E的父类型。 叫下限。
上限示例代码:
importjava.util.ArrayList;
importjava.util.Collection;
importjava.util.Iterator;
import bean.Person;
importbean.Student;
importbean.Worker;
public class GenericDemo4 {
    public static void main(String[] args) {
       ArrayList<Worker> al1 = new ArrayList<Worker>();
       al1.add(new Worker("工人2",20));
       al1.add(new Worker("工人4",40));
       ArrayList<Student> al2 = new ArrayList<Student>();
       al2.add(new Student("小明1",20));
       al2.add(new Student("小四4",24));
       printCollection(al1);
       printCollection(al2);
    }
    public static void printCollection(Collection<? extends Person> al1) {
       Iterator<? extends Person> it = al1.iterator();
       while(it.hasNext()){
           System.out.println(it.next());
       }
    }
}

什么时候使用上限呢?
一般在存储元素的时候都使用上限,因为这样取出都是按照上限类型来运算的,不会出现类型安全隐患。

注意:迭代器的泛型百分之百和获取迭代器对象集合的泛型一致。

什么时候使用下限?
通常对集合中的元素进行取出操作时,可以使用下限。

下限代码示例:
importjava.util.ArrayList;
importjava.util.Collection;
import java.util.Iterator;
importbean.Person;
importbean.Student;
importbean.Worker;
public class GenericDemo5 {
    /**
     * @param args
     */
    public static void main(String[] args) {
       ArrayList<Worker> al1 = new ArrayList<Worker>();
       al1.add(new Worker("工人1",10));
       al1.add(new Worker("工人3",30));
       ArrayList<Student> al2 = new ArrayList<Student>();
       al2.add(new Student("小明1",20));
       al2.add(new Student("小明4",30));
       ArrayList<Person> p2 = new ArrayList<Person>();
       p2.add(new Person("人民1",40));
       p2.add(new Person("人民2",59));
       printCollection(al1);
       printCollection(p2);
//     printCollection(al2);
    }
    public static void printCollection(Collection<? super Worker> al1) {
       Iterator<? super Worker> it = al1.iterator();
       while(it.hasNext()){
           System.out.println(it.next());
       }
    }
}



1.4 集合使用技巧


集合查阅的一些技巧:
需要唯一吗?
需要:Set
         需要制定顺序:
                            需要:TreeSet
                            不需要:HashSet
                            但是想要一个和存储一致的顺序(有序):LinkedHashSet

不需要:List
         需要频繁增删吗?
                   需要:LinkedList
                   不需要:ArrayList

如何记录每一个容器的结构和所属体系呢?
看名字!
List
         |----ArrayList
         |----LinkedList
Set
         |----HashSet
         |----TreeSet
后缀名就是该集合的所属的体系。
前缀名就是该集合的数据结构。
看到array:就要想到数组,就要想到查询快,有角标。
看到link:就要想到链表,就要像到增删快,就要想到 add  get remove+first last 的方法
看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法。
看到tree:就要想到二叉树,就要想到排序,就要想到两个接口Comparable,Comparator。

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


1.5 foreach


foreach语句:
格式: for(类型 变量:Collection集合|数组)
          {

          }
传统for和高级for的区别?
传统for可以完成对语句执行很多次,因为可以定义控制循环的增量和条件。
高级for是一种简化形式。
它必须有被便利的目标。该目标要么是数组,要么是Collection单列集合。
对于数组的遍历如果仅仅是获取数组中的元素,可以使用高级for。
如果要对数组的角标进行操作,建议使用传统for。

可以使用高级for遍历Map集合吗?
不能直接用,但是可以将Map转成单列的Set集合,就可以用了。

函数的可变参数。
其实就是一个数组,但是收的是数组的元素。
自动将这些元素封装成数组。简化了调用者的书写。
注意:可变参数类型,必须定义在参数列表的结尾


集合练习


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


importjava.util.Iterator;
importjava.util.Map;
importjava.util.TreeMap;

/*
* 练习
* "fdgavcbsacdfs" 获取该字符串中,每一个字母出现的次数。
* 要求打印结果是:a(2)b(1)...;
* 思路:
* 对于结果的分析发现,字母和次数之间存在着映射关系。而且这种关系很多。
* 很多就需要存储,能存储映射关系的容器有数组和Map集合。
* 关系一方式中是有序编号吗?有就使用数组,没有!那就是使用Map集合。
* 又发现可以保证唯一性的一方具备着顺序如a b c.....
* 所以可以使用TreeMap集合。
*
* 这个集合中应该存储的是字母和次数的对应关系。
*
* 1,因为操作的是字符串中的字母,所以先将字符串变成一个字符数组。
* 2,遍历字符数组,用每一个字母作为键去查Map集合这个表。
* 如果该字母键不存在,就将该字母作为键1作为值存储到Map集合中,代表出现一次。
* 如果该字母键存在,就将该字母键对应值取出并自增,再将该字母和自增后的值存储到Map集合中,
* 键相同值会覆盖,这样就记录住了该字母的次数。
* 3,遍历结束,Map集合就记录所有字母的出现的次数。
*/
public class MapTest1 {


    /**
     * @param args
     */

    public static void main(String[] args) {
       String s = "fdgavcbsacdfs";
       String str = getCharCount(s);
       System.out.println(str);
    }

    public static String getCharCount(String s) {
       //将字符串变成数组
       char []  ch = s.toCharArray();
       //建立Map集合,通过循环来记录住出现的字符和次数来对Map集合查表,没有出现的字符就存储,出现的字符就继续自增value值
       //看输出的结果是有序的打印,因此可以用TreeMap来完成
       Map<Character,Integer> map = new TreeMap<Character,Integer>();
       for (int i = 0; i < ch.length; i++) {
           Integer value = map.get(ch );
           if(value==null){
              map.put(ch , 1);
           }
           else
              map.put(ch , 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();
    }
}



练习:
将"asjd+    5568a sd SAAA Aas77dfffwe"字母出现的次数打印出来
要求打印结果是:a(2)b(1)...;


importjava.util.Iterator;
importjava.util.Map;
importjava.util.TreeMap;

public class MapTest3 {

    /*
     * 需求:
     * "asjd    5568a sd SAAA Aas77dfffwe"获取该字符串中,每一个字母出现的次数
     * 要求打印结果是:a(2)b(1)...;
     */
    /**
     * @param args
     */
    public static void main(String[] args) {
       String str = "asjd+    5568a sd SAAA Aas77dfffwe";
       String s = getCharCount(str);
       System.out.println(s);
    }

    public static String getCharCount(String str) {
       //将字符串变成数组
       char [] chs = str.toCharArray();
       //建立以个Map集合,通过循环来查询Map集合表。
       Map<Character,Integer> map = new TreeMap<Character,Integer>();
       for (int i = 0; i < chs.length; i++) {
           //判断一下,如果不大于或等于里面里面的字符就不打印,排除了里面的符号和空格
           if(!(chs >='a' && chs<='z' || chs>='A' && chs<='Z'))
              continue;
           Integer value = map.get(chs );
           if(value==null){
              map.put(chs ,1);
           }else{
              map.put(chs , 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();
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值