零基础学习java------day14-----泛型,foreach,可变参数,数组和集合间的转换,Set,Map,
1.泛型(jdk1.5以后出现)
https://www.cnblogs.com/lwbqqyumidi/p/3837629.html#!comments
(1)为什么要用泛型?
限制集合,让它只能存储某种类型的元素,如果不限制,集合(默认Object类型)中想存什么就存什么,这样在取元素的时候就会面临大量强制类型转换,这就很可能出现转换异常,为了解决这个问题,jdk1.5以后就出现泛型
当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。因此,当我们取出集合元素时需要人为的强制类型转化到具体的目标类型,这就很容易很容易出现“java.lang.ClassCastException”异常
(2)泛型概述
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变化量话参数,此时类型也定义成参数形式(可称之为类型形参),然后在使用/调用时传入具体的类型(类型实参),一般用"<E>"表示。泛型只在编译时有效,在生成的.class文件中,会把泛型擦除掉。
(3)泛型定义
a 定义在类上
若类在定义时没有加泛型时,则在创建对象时,不能使用泛型,如下图
此时Box定义时加上泛型,如下
public class GenericDemo { public static void main(String[] args) { Box<Integer> box = new Box<>(2); //右边的尖括号内不需要写具体的可类型,jdk.7以后会根据左边自动识别出类型是什么 } } class Box<T>{ T t; // T的类型是什么由创建对象时确定,如此处T就是Integer类型 public Box(T t) { this.t = t; } public T getT() { return t; } }
注意,类在创建对象时,不加泛型,默认是Object类型
b 定义在方法上(不常用)
用法:方法的逻辑相同,只是数据类型不同,这个时候使用反省方法,泛型写在返回值之前,以Collections中sort方法的源码为例
public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
用了泛型后,sort方法中List中的参数类型就可以不同
public class GenericDemo { public static void main(String[] args) { String str = cast("aaa"); } public static<E> E cast(Object o) { return (E)o;// 强制类型转换,向下转型 } }
c. 泛型定义在接口上
interface A<E>{ public void test(E e); } // 如果实现的接口没有指定具体的类型,则子类必须泛型下去 class B<E> implements A<E>{ public void test(E e) { } } // 如果实现的接口给定了具体的类型,子类中用到泛型的地方都必须使用该类型 class C implements A<String>{ @Override public void test(String e) { } }
(4)泛型通配符
泛型通配符:
泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的java类了
?extends E
向下限定,E及其子类
?super E
向上限定,E及其父类
注意:泛型的通配值只能用在=的左边或者是参数列表上
public class GenericDemo2 { public static void main(String[] args) { Collection<?> c1 = new ArrayList<Animal>(); Collection<?> c2 = new ArrayList<Dog>(); Collection<?> c3 = new ArrayList<Cat>(); Collection<?> c4 = new ArrayList<Object>(); Collection<? extends Animal> c5 = new ArrayList<Animal>(); Collection<? extends Animal> c6 = new ArrayList<Dog>(); Collection<? extends Animal> c7 = new ArrayList<Cat>(); //Collection<? extends Animal> c8 = new ArrayList<Object>();报错 Collection<? super Animal> c9 = new ArrayList<Animal>(); // Collection<? super Animal> c10 = new ArrayList<Dog>();报错 // Collection<? super Animal> c11 = new ArrayList<Cat>();报错 Collection<? super Animal> c12 = new ArrayList<Object>(); } } class Animal {} class Dog extends Animal {} class Cat extends Animal {}
2. foreach(增强for循环)
简化数组和Collection集合的遍历
格式:
for(元素数据类型 元素名 :数组或者Collection集合){对元素进行你自己的操作}
好处:简化遍历
注意事项:增强for的目标要判断是否为null
数组遍历
public class ForeachClass { public static void main(String[] args) { String[] str = new String[] {"哈","哈"};//当此处的数组为null时就会报java.lang.NullPointerException错误,所以要加验证,如下集合例子 for(String a:str) { System.out.println(a); } } }
集合的遍历
public class ForeachClass { public static void main(String[] args) { List<Person> ps = new ArrayList<>(); ps.add(new Person("张三",27)); ps.add(new Person("张一",17)); ps.add(new Person("李四",37)); ps.add(new Person("老王",67)); ps.add(new Person("小红",25)); if(ps!=null && ps.size()>0) { for(Person p:ps) { System.out.println(p); } } } } class Person{ String name; int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
3. 可变参数
定义方法的时候不知道该定义多少个参数
格式:
修饰符 返回值类型 方法名(数据类型··· 变量名)
注意:
这里的变量其实是一个数组
如果一个方法有可变参数,并且有多个参数,那么,可变参数必须是最后一个(若放前面的话,不能确定哪个参数时不可变参数)
案例
public class ChangeableParamDemo { public static void main(String[] args) { System.out.println(getSum(1,2,33)); } public static int getSum(int b, int...a) { //此变量a为数组 int sum = 0; System.out.println(b); for(int i=0;i<a.length;i++) { sum += a[i]; } return sum; } }
// 运行结果:
1
35
4. 数组和集合间的转换
1.集合转数组;
toArray(collection): 得到 Object[ ]
2.数组转集合:
案例
public class ArrayAndCollection { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("老王"); list.add("小明"); //1 集合转数组 Object[] o = list.toArray(); System.out.println(o); //2 数组转集合 String[] str = {"1","2"}; List<String> list1 = Arrays.asList(str); System.out.println(list1 instanceof List);//true } }
注意,使用alist得到的集合是不能进行add和remove操作的(只能是可读的集合,
原因:此处asList返回的是一个ArrayList对象,但这个ArrayList是Arrays里面的内部类,这个类中没有实现增加和删除的方法
如果非要操作,需要创建一个新的集合,把它传入到构造方法中,如下
Integer[] arr = {1,2}; List<Integer> list1 = Arrays.asList(arr); List<Integer> list2 = new ArrayList<>(list1); list2.add(100); System.out.println(list2);//[1, 2, 100]
5.Set
(1)
特点:Set的元素时无序的(不能使用索引操作元素),元素不可重复
collection:
List(): 有序的,元素可重复
ArrayList
LinkedList:特有方法(增删,获取集合首个和最后一个元素)
Set(): 无序的(不能使用索引操作元素),元素不能重复
HashSet:无法保证存入和取出的顺序
LinkedHashSet:可以保证存入和取出的顺序(链表实现)
TreeSet:有序的(可以对元素排序)
不可重复性的原理:
是由hashCode和equals方法保证的
存放元素的时候,先求出元素的hashCode,如果集合中没有这样的hashCode值,说明该元素在集合中不存在,可以存;有这样的hashCode,再比较equals:如果为true,集合中已经存在噶元素,则不存,如果为false,则可以存。
案例
public class SetDemo { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("小猫警长"); set.add("黑猫警长"); set.add("白猫警长"); set.add("花猫警长"); set.add("白猫警长"); set.add("花猫警长"); set.add("黑猫警长"); set.add("红猫警长"); System.out.println(set); } } // 运行结果:[黑猫警长, 红猫警长, 白猫警长, 花猫警长, 小猫警长]
可见存入的顺序并不有序,若想有序可以使用LinkedHashSet创建对象
(2)Set的遍历(三种方法)
第一种:数组
public class SetDemo { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("小猫警长"); set.add("黑猫警长"); set.add("白猫警长"); set.add("花猫警长"); set.add("红猫警长"); Object[] array = set.toArray(); for(Object s:array) { System.out.println(s); } } }
第二种:迭代器
Iterator<String> it = set.iterator(); while(it.hasNext()) { System.out.println(it.next()); }
第三种(增强for循环)
for(String str :set) { System.out.println(str); }
练习:
1. 使用LinkedHashSet填写一个程序,获取10个1至20的随机数,要求随机数不能重复
public class TenNum { public static void main(String[] args) { Set<Integer> set = new LinkedHashSet<>(); Random r = new Random(); while(set.size()<10) { int num = r.nextInt(20)+1; set.add(num); } System.out.println(set); } }
2. 使用集合去重
public class SetQuChong { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(1); list.add(2); list.add(2); List<Integer> list1 = distinct(list); System.out.println(list1); } public static List<Integer> distinct(List<Integer> list){ Set<Integer> set = new HashSet<>(); set.addAll(list); list.clear(); list.addAll(set); return list; } } // 运行结果:[1,2]
6. Map
6.1 概述
双列集合的跟接口;将键映射到值的对象;一个映射不能包含重复的键,每个键最多只能映射到一个值。
Map接口和Collection接口的不同:
Map是双列的,Colection是单列的
Map的键唯一(值可以不唯一),Collection的子体系只有Set是唯一的
6.2 Map接口的成员方法
V put(K key,V value) 添加元素(返回值是Key上一次对应的Value) V remove(Object key) 移除key对应的键值对(返回值是value) void clear() 清空map boolean containsKey(Object key) 是否包含key boolean containsValue(Object value) 是否包含value boolean isEmpty() 是否为空 int size() 键值对的个数
V put(K key,V value) 添加元素(返回值是Key上一次对应的Value)
public class MapDemo { public static void main(String[] args) { Map<String,String> map = new HashMap<>(); String re = map.put("a","啊,五环,你比四环多一环"); System.out.println(re); String re1 = map.put("a","啊,四环,你比五环少一环"); System.out.println(re1); } } //运行结果: null 啊,五环,你比四环多一环
由结果可知,put的返回值为当次被覆盖的内容(第一次打印的结果为null,相当于第一次添加值将null覆盖)
V remove(Object key) 移除key对应的键值对(返回值是被移除键值对中的value)
System.out.println(map);//{a=啊,四环,你比五环少一环} String re2 = map.remove("a"); System.out.println(re2);//啊,四环,你比五环少一环
get(key) 方法(可用于遍历),返回值为key所对应的值
System.out.println(map);//{a=啊,四环,你比五环少一环} String re3 = map.get("a"); System.out.println(re3);//啊,四环,你比五环少一环
6.3 Hashmap的遍历
第一种 keySet 获取所有key
public class MapDemo { public static void main(String[] args) { Map<Integer,String> map = new HashMap<>(); map.put(1, "小明"); map.put(2, "小红"); map.put(3, "小王"); map.put(4, "小李"); Set<Integer> keySet = map.keySet(); for(Integer key:keySet) { System.out.println("key:"+key+",value:"+map.get(key)); } } }
第二种 entrySet(获取键值对)
Set<Entry<Integer, String>> entrySet = map.entrySet(); for (Entry<Integer, String> entry:entrySet) { System.out.println("key:"+entry.getKey()+",value:"+entry.getValue());
}
使用迭代器实现:
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while(it.hasNext()) { System.out.println(it.next()); System.out.println("key:"+next.getKey()+",value:"+next.getValue()); }
第三种 values:只能得到所有的value
Collection<String> values = map.values(); for (String v :values) { System.out.println(v); }
6.4 HashMap的实现原理
使用散列表(由数组和链表组成)或哈希表来实现的(效率高)
数组+链表:元素是链表的数组(主体是数组)
练习:统计一个字符串中各个字符出现的次数
public class StrTimes { public static void main(String[] args) { Scanner sc= new Scanner(System.in); System.out.println("请输入一个字符串"); String str = sc.nextLine(); // 创建map,用来存储数据 Map<Character, Integer> map = new HashMap<>(); // 遍历获取每一个字符 for(int i=0;i<str.length();i++) { char ch = str.charAt(i); //用ch做key去map中查询是否有值 Integer value = map.get(ch); if(value == null) { map.put(ch, 1); }else { value++; map.put(ch,value); } } Set<Character> keySet = map.keySet(); for(Character c:keySet) { System.out.println("字符"+c+"的次数:"+map.get(c)); } } }