双列集合、泛型的笔记
------- android培训、java培训、期待与您交流! ---------
1 双列集合
Map是一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。在Map中键(key)可以使任意类型的对象。Map中不能有重复的键(Key),每个键(key)都有一个对应的值(value)。一个键(key)和它对应的值构成map集合中的一个元素。
Map中的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复。
interface Map<K,V>
K - 此映射所维护的键的类型
V - 映射值的类型
概念
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
1.1 Map学习体系:
Map学习体系: ---| Map 接口 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 ---| HashMap 采用哈希表实现,所以无序 ---| TreeMap 可以对健进行排序 |
---|Hashtable: 底层是哈希表数据结构,线程是同步的,不可以存入null键,null值。 效率较低,被HashMap 替代。 ---|HashMap: 底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。 要保证键的唯一性,需要覆盖hashCode方法,和equals方法。 ---| LinkedHashMap: 该子类基于哈希表又融入了链表。可以Map集合进行增删提高效率。 ---|TreeMap: 底层是二叉树数据结构。可以对map集合中的键进行排序。需要使用Comparable或者Comparator 进行比较排序。return 0,来判断键的唯一性。 |
1.2常见方法
1、添加: 1、V put(K key, V value) (可以相同的key值,但是添加的value值会覆 盖前面的,返回值是前一个,如果没有就返回null) 2、putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关 系复制到此映射中(可选操作)。 2、删除 1、remove() 删除关联对象,指定key对象 2、clear() 清空集合对象 3、获取 1:value get(key); 可以用于判断键是否存在的情况。当指定的键不存在的时候,返 回的是null。
3、判断: 1、boolean isEmpty() 长度为0返回true否则false 2、boolean containsKey(Object key) 判断集合中是否包含指定的key 3、boolean containsValue(Object value) 判断集合中是否包含指定的value 4、长度: Int size()
|
1.3遍历Map的方式:
1、将map 集合中所有的键取出存入set集合。 Set<K> keySet() 返回所有的key对象的Set集合 再通过get方法获取键对应的值。 2、 values() ,获取所有的值. Collection<V> values()不能获取到key对象 3、 Map.Entry对象 推荐使用 重点 Set<Map.Entry<k,v>> entrySet() 将map 集合中的键值映射关系打包成一个对象 Map.Entry对象通过Map.Entry 对象的getKey, getValue获取其键和值。 |
public class Demo2 {
public static void main(String[] args) { Map<String,String> map = new HashMap<String, String>(); map.put("汪峰", "章子怡"); map.put("文章", "马伊琍"); map.put("王军", "美美");
//遍历Map集合的方式1:可以使用keySet方法遍历。 缺点: keySet方法只能获取到所有的键,不能获取到所有的值。 Set<String> keys = map.keySet(); // 把Map中的所有键都存储到一个Set的集合对象返回。 Iterator<String> it = keys.iterator(); while(it.hasNext()){ String key = it.next(); System.out.println("键:"+ key+" 值:"+ map.get(key)); }
//遍历Map的方式2:使用values方法遍历。 缺陷:只能获取到所有的值。不能获取到键。 Collection<String> values = map.values(); Iterator<String> it1 = values.iterator(); while(it1.hasNext()){ System.out.println("值:"+ it1.next()); }
//遍历Map集合的方式3:使用entrySet方法遍历。重点 Set<Map.Entry<String,String>> entrys = map.entrySet(); Iterator<Map.Entry<String,String>> it2 = entrys.iterator(); //获取迭代器 while(it2.hasNext()){ Entry<String,String> entry = it2.next(); //一个entry对象就是一条map的数据。 System.out.println("键:"+ entry.getKey()+" 值:"+ entry.getValue()); } } } |
1.4HashMap
HashMap的存储原理:
HashMap 也是哈希表实现的,往HashMap存储元素的时候,HashMap会调用键的hashCode方法得到一个哈希码,然后通过哈希码算出该元素在哈希表中存储位置。 情况1:根据键的哈希码算出的位置如果目前没有存在任何元素,那么该元素可以直接存储到哈希表中。 情况2:根据键的哈希码算出的位置目前已经存在有其他元素,那么还会调用键的equals方法与这个位置的元素再进行一次比较,如果equals方法返回的是true,那么该元素不允许存储,如果equals方法返回的是false,那么该元素允许存储。 |
class Person{ int id; String name; public Person(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "编号:"+ this.id+" 姓名:"+ this.name; } @Override public int hashCode() { return this.id; } @Override public boolean equals(Object obj) { Person p = (Person)obj; return this.id == p.id; } }
public class Demo3 { public static void main(String[] args) { //创建一个HashMap对象 HashMap<Person, String> map = new HashMap<Person, String>(); //存储元素 map.put(new Person(110,"狗娃"),"001"); map.put(new Person(119,"狗剩"),"002"); map.put(new Person(220,"铁蛋"),"003");
//如果id号一致,那么就视为同一个人。 map.put(new Person(220,"陈铁"),"004"); System.out.println(map); } } |
1.5 TreeMap
TreeMap的存储原理
1、 往TreeMap添加元素的时候,如果键具备自然顺序的特征,TreeMap会根据元素的键进行排序存储 2、 往TreeMap添加元素的时候,如果键不具备自然顺序的特征,那么键所属的类必须实现Comparable接口,重写compareTo方法,也就是让元素自身具备比较性。 3、 往TreeMap添加元素的时候,如果键不具备自然顺序的特征,键所属的类也没有实现Comparable接口,那么在创建TreeMap对象时传入比较器的对象。 |
注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;
class Emp{ //implements Comparable<Emp>{ String name; int salary; public Emp(String name, int salary) { this.name = name; this.salary = salary; } @Override public String toString() { return "姓名:"+ this.name+" 薪水:"+ this.salary; }
/*@Override public int compareTo(Emp o) { return this.salary - o.salary; }*/ }
//自定义一个比较器 class SalaryComparaTor implements Comparator<Emp>{ @Override public int compare(Emp o1, Emp o2) { return o2.salary - o1.salary; } }
public class Demo4 { public static void main(String[] args) { TreeMap<Emp, String> tree = new TreeMap<Emp, String>(new SalaryComparaTor()); tree.put(new Emp("老蔡",1800), "001"); tree.put(new Emp("老钟",800), "007"); tree.put(new Emp("老超",1000), "005"); System.out.println(tree); } } |
2 Collections与Arrays
集合框架中的工具类:特点:该工具类中的方法都是静态的。
Collections:常见方法: 1, 对list进行二分查找: 前提该集合一定要有序。 int binarySearch(list,key); //必须根据元素自然顺序对列表进行升级排序 //要求list 集合中的元素都是Comparable 的子类。 int binarySearch(list,key,Comparator); 2,对list集合进行排序。 sort(list); //对list进行排序,其实使用的事list容器中的对象的compareTo方法 sort(list,comaprator); //按照指定比较器进行排序 3,对集合取最大值或者最小值。 max(Collection) max(Collection,comparator) min(Collection) min(Collection,comparator) reverse(list); 5,对比较方式进行强行逆转。 Comparator reverseOrder(); Comparator reverseOrder(Comparator); 6,对list集合中的元素进行位置的置换。 swap(list,x,y); 7,对list集合进行元素的替换。如果被替换的元素不存在,那么原集合不变。 replaceAll(list,old,new); 8,可以将不同步的集合变成同步的集合。 Set synchronizedSet(Set<T> s) Map synchronizedMap(Map<K,V> m) List synchronizedList(List<T> list) 9. 如果想要将集合变数组: 可以使用Collection 中的toArray 方法。注意:是Collection不是Collections工具类 传入指定的类型数组即可,该数组的长度最好为集合的size。 |
Arrays:用于对数组操作的工具类
1,二分查找,数组需要有序 binarySearch(int[]) binarySearch(double[])
2,数组排序 sort(int[]) sort(char[])…… 2, 将数组变成字符串。 toString(int[]) 3, 复制数组。 4, 复制部分数组。 copyOfRange(): 5, 比较两个数组是否相同。 equals(int[],int[]); 6, 将数组变成集合。 List asList(T[]); 这样可以通过集合的操作来操作数组中元素, 但是不可以使用增删方法,add,remove。因为数组长度是固定的,会出现 UnsupportOperationExcetion。 可以使用的方法:contains,indexOf。。。 如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。 如果数组中的存入的引用数据类型,那么asList会将数组中的元素作为集合中 的元素。 |
public class Demo1 { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(2); list.add(12); list.add(1); list.add(22);
Collections.sort(list); //排序
int index = Collections.binarySearch(list, 12); System.out.println("找到的索引值:"+ index);
System.out.println("最小值:"+ Collections.min(list)); System.out.println("最大值:"+ Collections.max(list));
//翻转 Collections.reverse(list); Collections.swap(list, 1, 2); //指定两个索引值交换位置 Collections.replaceAll(list, 12, 120); List list2 = Collections.synchronizedList(list); System.out.println(list); } } |
集合的练习
问题: 定义一个Person数组,将Person数组中的重复对象剔除?
思路:
1. 描述一个Person类
2. 将数组转换为Arrays.asList() List
3. Set addAll( list )
4. hashCode()且equals()
import java.util.Arrays; import java.util.Set; import java.util.List; import java.util.HashSet;
// 1. 描述Person类 class Person { public String name; public int age;
public Person() { }
public Person(String name, int age) { this.name = name; this.age = age; }
public String toString() {
return getClass().getName() + " : name=" + this.name + " age=" + this.age;
}
// 4. 重写hashCode和equals() public int hashCode() {
return this.age; }
public boolean equals(Object o) { Person p = null; if (o instanceof Person) p = (Person) o; return this.name.equals(p.name) && (this.age == p.age); } }
class Demo { public static void main(String[] args) { Person[] ps = new Person[] { new Person("jack", 34), new Person("lucy", 20), new Person("lili", 10), new Person("jack", 34) }; // 遍历数组 System.out.println(Arrays.toString(ps)); // 2. 将自定义对象数组转换为List集合 List<Person> list = Arrays.asList(ps); // 3. 将List转换为Set Set<Person> set = new HashSet<Person>(); set.addAll(list); System.out.println(set);
} } |
3 泛型
3.1 泛型的概述
泛型: 泛型是jdk1.5出现的新特性。
泛型的好处:
1.可以把运行时的问题提前至编译时。
2.避免了无谓的强制类型转换。
泛型在集合中的应用:
ArrayList<String> list = newArrayList<String>();
ArrayList list = newArrayList<String>();
ArrayList<String> list = newArrayList();
注意:在泛型中是没有多态的概念的,两边的数据类型要求一致,或者是只写一边的泛型,另外一边不写。
推荐使用: 两边都写上泛型。
3.2自定义泛型:
自定义泛型我们可以理解就是一个数据类型的占位符或者是一个数据类型的变量。
泛型方法:
修饰符 <声明自定义的泛型>返回值类型 函数名(形参...){
}
泛型方法要注意的事项:
1. 泛型方法中的自定义泛型的具体数据类型是在调用该方法的时候传入参数的时候确定。 2. 自定义泛型所用到的标识符可以随意的,只要符合标识符的命名规则即可。 |
泛型不能使用基本数据类型数据,要使用基本数据对应包装类型。
int Integer
char Character
double Doule
float Float
booleanBoolean
3.3 泛型类
泛型类的定义格式:
class 类名<声明自定义的泛型>{
}
自定义泛型类要注意的事项:
1. 在类上自定义的泛型的具体数据类型是在使用这个类创建对象的时候确定 的。 2. 如果类上声明了自定义泛型,在创建对象的时候没有指定这个 泛型 的具体数据类型,那么默认为Object数据类型。 3. 在类上声明的自定义泛型不能应用于静态方法上。如果静态方法需要使用,那么只能在方法上自己声明自定义泛型。 |
//数组的工具类 class ArrayUtil<T>{ // 在类上声明了自定义泛型,
//接收任意类型的数组对象 public String toString(T[] arr){ StringBuilder sb = new StringBuilder(); for(int i = 0 ; i<arr.length ; i++){ if(i==0){ sb.append("["+arr[i]+","); }else if(i==arr.length -1){ sb.append(arr[i]+"]"); }else{ sb.append(arr[i]+","); } } return sb.toString(); }
public static <T> void test(T t){ System.out.println(t); } //翻转数组元素 public void reverse(T[] arr){ for(int startIndex = 0 , endIndex = arr.length -1 ; startIndex<endIndex ; startIndex++,endIndex--){ T temp = arr[startIndex]; arr[startIndex] = arr[endIndex]; arr[endIndex] = temp; } } }
public class Demo3 {
public static void main(String[] args) { Integer[] arr = {10,20,30,40,50}; String[] arr2 ={"abc","aa"};
ArrayUtil util = new ArrayUtil(); util.reverse(arr2); System.out.println(util.toString(arr));
ArrayList<String> list = new ArrayList<String>(); } } |
3.4 泛型接口
泛型接口的定义格式:
interface 接口名<声明自定义泛型>{
}
泛型接口要注意的事项:
1. 泛型接口上面的自定义泛型是在实现该接口的时候确定。 2. 如果一个类实现一个泛型接口的时候没有指定具体的数据类型,那么泛型接口上的自定义泛型默认的数据类型是Object类型。 |
如果一个类想在创建接口实现类对象的时候再指定自定义泛型的具体数据类型,那么在实现接口的类的类名和接口名后需表明泛型标志。然后在实现类中方法使用泛型标志即可
class User{} interface Dao<T>{ public void add(T t); public void delete(T t); } public class Demo2<T> implements Dao<T>{ public static void main(String[] args) { Demo2<User> d = new Demo2<User>(); } @Override public void add(T t) { // TODO Auto-generated method stub } @Override public void delete(T t) { // TODO Auto-generated method stub } } |
3.5 泛型通配符
? 泛型的通配符,可以匹配任意 的数据类型。
? super Integer 泛型的下限 泛型的下限可以使用Integer以及Integer父类类型数据。
? extends number 泛型上限 可以使用Number类型或者是Number的子类数据。
public class Demo3 { public static void main(String[] args) { ArrayList<Number> list = new ArrayList<Number>(); print(list); test(list);
HashSet<Integer> set = new HashSet<Integer>(); print(set); test(set);
}
//需求2:定义一个函数可以接收任意类型的单例集合对象,接收的单例集合对象只能存储Number数据类型或者是Numer子类类型数据。 public static void test(Collection<? extends Number> c){
}
public static void print(Collection<? super Integer> c){
} } |