黑马程序员——java基础学习---泛型、Map集合及扩展、高级for循环

时间:2023-02-18 07:56:55
------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


1、泛型

泛型是JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制。

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

这就表示这个集合存入的类型为String类。
  1)泛型好处:

将运行时期出现的问题ClassCastException,转移到编译时期,方便与程序员解决问题,让运行问题减少,更安全;

避免强制转换的麻烦。

如:迭代器:Terator<String> it =  a1.iterator();

则不需强转类型:String  s =it.next();
  2)泛型的格式:

通过<>来定义要操作的引用数据类型。
  3)在使用java提供的对象时,什么时候写泛型呢?

通常在集合框架中很常见,只要见到<>就是要定义泛型,<>就是用来接受引用数据类型的。
  4)注意:

A、用equals覆写Object时,因其没有泛型,所以在用时要强转类型,在转之前还要判断一下是否同类,即:instanceof。

B、泛型类: 当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成。但是在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。

C、除了定义泛型类,还可以定义泛型方法,泛型方法可以放到泛型类里面,即泛型类里还可以有泛型方法定义。

D、静态方法不可以访问类上定义的泛型,如果静态方法操作的应用数据类型不确定,可将泛型定义在方法上。

如:public  static  <T>  void  method(T  t){}

E、可定义泛型接口:

如:interface  Inter<T>

    {

              Void  show(T t);

    }

在实现泛型接口时,可以在实现就确定接口类型:

 如:   implements  Inter<String>
  5)泛型限定

当传入的类型不确定时,可以使用通配符?,也可以理解为占位符<?>。使用通配符的好处是可以不用明确传入的类型,这样在使用泛型类或者泛型方法时,提高了扩张性。

对于一个范围内的一类事物,可以通过泛型限定的方式定义:

两种方式:

 A、? extends E:可接收E类型或E类型的子类型,称为上限定。

   如:ArrayList<? extends Number>  x =  new ArrayList<Integer>();

B、? super E:可接收E类型或E类型的父类型,称为下限定。

    如:ArrayList<? super Integer>  x =  new ArrayList<Number>();
2、Map集合

Map<K,V>集合是一个接口,它与List、set集合不同的是,它是双列集合,以键值对的形式存储。集合存储键值对,是一对一对往里存,而且要保证键的唯一性。
  1)Map集合的子集

Hashtable:底层是哈希表数据结构,不可以存入null键和null值,该集合是线程同步的,效率低。

HashMap:底层是哈希表数据结构,允许使用null键和null值,该集合是线程不同步的,效率高,JDK1.2版本出现,替代了Hashtable。

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

Map和Set很像。其实Set底层就是使用了Map集合。
  2)Map集合共性方法

A、添加

  V put(K key,V value):添加元素,添加时如果出现相同的键,那么后添加的值会覆盖原有键对应值,并put方法会返回被覆盖的值;

  void  putAll(Map<? extends K,? extends V> m):添加一个集合;

B、删除

  clear():清空集合;

  V remove(Object key):删除指定键值对,若不存在,返回null;

C、判断

  containsValue(Object value):判断值是否存在;

  containsKey(Object key):判断键是否存在;

  isEmpty():判断集合是否为空;

D、获取

  V get(Object key):通过键获取对应的值,若无返回null;

  size():获取集合的长度;

  Collection<V> values():获取集合中所有值,返回个Collection集合;

   还有两个取出方法,很重要:

    Set<Map.Entry<K,V>> entrySet():返回映射中含映射关系的集合;

 Set<K> keySet():返回含键的集合。


  3)Map的两种取出方式:

A、keySet( ):将Map中所有的键存入到Set集合,因为Set集合具备迭代器,所以可以通过迭代方式取出所有键值,再通过get(key)方法,获取每一个键对应的值。

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

B、entrySet():将Map集合中的映射关系存入到Set集合中,而这个关系的数据类型就是Map.Entry;

关于Map.Entry:其实Entry也是一个接口,它是Map接口中的一个内部接口。

为何要定义在其内部呢?

a、Map集合中存的是映射关系这样的两个数据,是先有Map这个集合,才可有映射关系的存在,而且此类关系是集合的内部事务;

b、这个映射关系可直接访问Map集合中的内部成员,所以定义在内部。

Map.Entry<k,v>接口含有方法:

  getKey();getValue();setValue()。


综合示例:

学生Student,地址String,每一个学生都有对应的归属地; 

学生属性:姓名,年龄; 

保证学生的唯一性:姓名和年龄相同的视为同一个学生。

思路:1、先描述学生;

      2、定义map集合,将学生作为键,地址作为值存入;

      3、获取map集合中的元素。 

import java util.*;

class Student implementsComparable<Student> 

{

private String name;  

 private int age;  

 Student(String name,int age)  

 { 

      this.name=name; 

      this.age=age;  

 } 

 public int compareTo(Student  s)

{

       int num =

          new Integer(this.age).compareTo(newInteger(s.age));

       if(num==0)

            returnthis.name.compareTo(s.name);

       return  num;

public int hashCode()

{

        return name.hashCode()+age*10;

}

public boolean equals(Object obj)

{

        if(!(obj instanceof Student))

            throw newException(“对象不是学生”);

        Student s = (Student)obj;

        Return this.name.equals(s.name)&& this.age==s.age;

}

public String getName()

{

      return name;

}

Public int getAge()

{

      Return age;

}

public String toString()

{

     return name+”:”+age;

}

}

class  MapDemo

{

       public static voidmain(String[] args)

       {

           HashMap<Student,String> hm =

                             new HashMap<Student,String>();

           hm.put(new Student(“zhangsan”,20),”shanghai”);

           hm.put(new Student(“lisi”,25),”beijing”);

           hm.put(new Student(“wangwu”,22),”guangzhou”);

      

           //第一种取出方式:

           Set<Student>  keyset = hm.keySet();

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

           while(it.hasNext())

           {

              Student stu =it.next();

              String addr =hm.get(stu);

              System.out.println(stu+”:”addr);

           }

           //第二种取出方式:

           Set<Map.Entry<Student,String>>  entrySet = hm.keySet();               Iterator<Map.Entry<Student,String>>iter =

                                             entrySet.iterator();

           while(iter.hasNext())

           {

              Map.Entry<Student,String> me = iter.next();

              String stu =me.getKey();

              String addr =me.getValue();

              System.out.println(stu+”:”addr);

           }

 

       }

}
  4)Map集合的扩展

A、什么时候用map集合?

  当数据之间存在着映射关系的时候,就先考虑使用Map集合。

B、在很多时候应用比较多的是一对多的映射关系,可以通过嵌套的形式将多个映射定义到一个大的集合中,然后分级处理,形成一个体系。
3、collections

Collections是集合框架的一个工具类,里边的方法都是静态的,不需要创建对象,可直接用类调用方法,其大部分方法是用于对List集合进行操作的,如比较、二分法搜索、随机排序等。 
  1)常见操作

A、查找

 max(Collection<?extends T> c):根据集合的自然顺序,获取c集合中的最大元素;

 max(Collection<?extends T> c,Comparator<? super T> co):根据指定比较器co的顺序,获取c集合中的最大元素;

 int binarySearch(Lsit<?extends Comparable<? super T>> list,Tkey):二分法搜索list集合中的指定对象;

   B、替换

 void fill(List<? superT> list, T obj):将list集合中的全部元素替换成指定对象obj;

 boolean replaceAll(List<T>lsit,T oldValue,T newValue):用newValue替换集合中的oldValue值;

 void swap(Listlist,inti,int j):在指定列表的指定位置处交换元素;

   C、排序

 void shuffle(List<?>list):使用默认随机源对list集合中的元素进行随机排序;

 void sort(Lsit<T>list):根据自然顺序对list集合中的元素进行排序;

 void sort(List<T>lsit,Comparator<? super T> c):根据指定比较器的排序方式对list集合进行排序;

   D、反转

 reverse(List<?>list):反转list集合中元素的顺序;

 Comparator reverseOrder():返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序;

 Comparator reverseOrder(Comparator<T>cmp);//返回一个比较器,强行逆转了指定比较器的顺序;
  2)Collections和Collection的区别:

 Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。

两个常用的子接口:

    List:可以重复元素,有序的,有索引;        

    Set:不可重复元素,无序的。

 Collections是集合框架中的一个工具类,类中的方法都是静态的,提供的方法中有可以对list集合进行排序,二分查找等方法。

 因为要提高效率,常用的集合通常都是线程不安全的,所以如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
4、Arrays

Arrays是集合框架工具类,用于操作数组,包含的都是静态方法。
  方法:

1)asList(arr[]):把arr数组变成List集合,这样做的好处就是可以使用集合的思想和方法来操作数组中的元素,如:contains(),get(),subList()等方法。

但是要注意,将数组变成集合,不可使用集合的增删方法,因为数组的长度是固定的,如果进行增删操作,会产生UnsupportedOperationException编译异常;如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。

2)String toString():可以接收各种数组类型参数,并返回指定数组内容的字符串表现形式。
5、toArray

<T> T[] toArray(T[] a):将集合转变成数组,是Collection接口中的方法,当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size;当指定类型的数组长度大于了集合的size,就不会新创建了数组,而是使用传递进来的数组,所以创建一个刚刚好的数组最优。

将集合变数组的作用:为了限定对元素的操作(不需要进行增删了)。
6、高级for循环

格式:

for(数据类型变量名: 被遍历的集合(collection)或数组) {执行语句}
  1)高级for循环对集合进行遍历,只能获取集合元素,但是不能对集合进行操作,迭代器除了遍历,还可以进行remove集合中元素的动作,如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。
  2)高级for和传统for的区别:

高级for循环有一个局限性,必须要有被遍历的目标,集合或数组;

建议在遍历数组的时候,还是使用传统for,因为可对角标定义操作。
7、可变参数

如果一个方法在参数列表中传入了多个参数,个数不确定,那么每次都要复写该方法,这时可以用数组作为形式参数,但是在传入时,每次都需要定义一个数组对象,作为实际参数。

在JDK1.5版本后,就提供了一个新特性:可变参数。

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

在使用时注意:可变参数一定要定义在参数列表的最后面。
8、静态导入

  1)写法:

importstatic java.util.Arrays.*;

                        导入Arrays这个类中的所有静态成员;

import static java.lang.System.*;

                        导入System类中所以静态成员。

注意:没加static导入的是类,加上static导入的是某一个类中所有的静态成员,这样在调用该类的静态方法时就可以不用再写类名。

如:Arrays.sort(数组)就可以直接写sort(数组)。
  2)当导入的两个类中有同名成员时,需要在成员前加上相应的类名;当类名相同时,就需要指定具体的包名;当方法重名时,需指定具体所属的对象或者类。