首页 > 代码库 > 黑马程序员_集合
黑马程序员_集合
集合
1、集合和对象数组的区别:
数组的长度不可变,集合的可变;
数组可以存储基本数据类型和对象,集合只能存储对象。
集合的框架图
集合派系的顶层接口Collection
1、Collection集合存储对象的方法:
add(E e)将元素存储到集合中
addAll(Collection c)将一个集合添加到另外的集合中
2、Collection集合提取对象的方法:
通过迭代器iterator中的方法;hasNext()和next()来取出
Iterator it=new iterator(); while(it.hasNext()){ System.out.println(it.next()); }
3、Collection集合移除对象的方法:
boolean remove (Object o)移除一个对象,移除成功返回true。
boolean removeAll(Collection c)移除两个集合中的共性内容,成功返回true。
4、Collection集合中的其他方法:
void clear()移除集合中所以元素
int size()返回集合中元素的个数
boolean contains(Object o)判断是否包含该对象,包含返回true。
boolean contains(Collection c)判断一个集合是否包含另一个集合,包含就返回true。
boolean retainAll(Collection c)将这两个集合的交集赋值给调用者,没有交集就是[]。
再判断调用者的元素是否改变,改变了就是true。
Collection中不常用的方法:
static reverse(List)反转集合
static fill(List,Object)替换集合
迭代器
1、概念:
是取出集合中存储对象的方式。
2、原理:
在接口Collection中,定义了一个方法 Iterator iterator(),是集合中所有子类都具备的。
iterator()返回的是一个Iterator接口类型,由于接口本身不能创建对象,所以返回的是接口的实现类对象
3、Iterator接口中的三个方法:
boolean hasNext()判断集合中还有没有元素,有返回true。
Object next() 获取集合中的元素,可以看成数组中的arr[i]。
void remove()移除遍历的集合中的元素
4、实现迭代器的三个步骤:
//通过集合对象的iterator()方法,返回一个Iterator接口的实现类对象
Iterator it=集合.iterator(); //使用迭代器对象的hasNext()方法,判断集合中还有没有对象可以被取出 while(it.hasNext()){ //使用迭代器对象的next()方法直接获取存在集合中的元素 it.next(); }
注:迭代器是获取集合中对象的通用方式。
5、注意事项:
a.如果迭代器已经获取玩了,再次获取,会出现没有元素被取出的异常。
b.在迭代的过程中不要使用集合的方法改变集合的长度。
并发修改异常ConcurrentMOdificationException:
原因:使用集合的方法修改了集合的长度,而迭代器不知道。
c:一次迭代中,不能多次出现next()方法,否则可能出现异常,出现数据错乱。
因为每用一次next(),指针就会往后移一次。
解决办法:Object o=迭代器.next();
List接口
1、特点:
有序:取出的顺序和存的时候一样
具有下标
允许存储重复元素
2、List集合存对象的方法:
add(int index,Objext o)index不能超过List集合原有的大小
3、List集合获取对象的方法
A、一般情况下也是通过iterator()来获取
但是List派系有特有的迭代器ListIterator,ListIterator接口是继承Iterator的。
ListIterator接口的特点:
在迭代的过程中可以对集合中的元素进行添加、修改和删除。
ListIterator接口的方法:
add(Object o)
set(Object o)
boolean hasPrevious()功能和hasNext()相同
Object previous()==next()
B、Object get(int index)获取指定下标的元素
4、List集合修改对象的方法:
Object remove(int index)
Object set(int index,Object Obj)
5、List集合其他方法:
List subList(int start,int end)
注:List集合由于继承关系的存在,也有Collection集合的添加、修改和删除等方法。后面学习
的子类也是这样。
ArrayList类
1、属于List派系下的子类,具备List派系的所有特点:
有序,下标,重复
ArrayList自己的特点:
底层数据结构是数组
底层是可变数组,导致了查询快,增删慢
数组的大小默认是10,每次增长50%
线程不同步,不安全,但执行效率高
注:是通过复制数组的形式实现数组的可变。
2、关于ArrayList集合的面试题
A、用户通过键盘输入姓名和成绩,姓名和成绩封装成学生对象,存到ArrayList中。
如果用户输入了"over",结束键盘的输入,迭代集合,迭代出已经存储的学生对象。
1 public class Person { 2 private String name; 3 private int age; 4 5 public void setName(String name){ 6 this.name=name; 7 } 8 public void setAge(int age){ 9 this.age=age;10 }11 public String getName(){12 return name;13 }14 public int getAge(){15 return age;16 }17 18 19 public Person(){20 21 }22 public Person(String name,int age){23 setName(name);24 setAge(age);25 }26 27 public String toString(){28 return "Person:"+name+"..."+age;29 }30 }31 import java.util.*;32 public class ArrayListTest {33 public static void main(String[] args) {34 //创建一个集合,创建一个键盘输入的Scanner35 ArrayList array = new ArrayList();36 Scanner sc = new Scanner(System.in);37 //接受键盘输入38 while(true){39 String str = sc.nextLine().trim();40 //判断输入的是不是over 41 if("over".equals(str))42 break;43 //进行字符串的切割操作,空格的形式44 String[] s = str.split(" +");45 //姓名和成绩存储到ArrayList集合,成绩转成int46 array.add(new Student(s[0],Integer.parseInt(s[1])));47 }48 //创建迭代器,获取集合中的元素49 Iterator it = array.iterator();50 while(it.hasNext()){51 System.out.println(it.next());52 }53 }54 }
B、制作一个6位的不同字符的验证码,输出到控制台,用户输入,判断是对还是错。
要求:6位不同的数子,字母和汉字。
import java.util.*;public class GetCode{ public static void main(String[] args){ Scanner sc=new Scanner(System.in); //定义数组,保存一些字符 char[] ch={‘a‘,‘v‘,‘1‘,‘3‘,‘6‘,‘你‘,‘黑‘,‘我‘,‘为‘,‘的‘,‘啊‘}; //创建一个字符串缓冲区 while(true){ StringBuilder sb=new StringBuilder(); //因为只需6个字符,所以sb的长度为6就停止添加字符 while(sb.length()<6){ int num=new Random().nextInt(ch.length); if(sb.toString().contains(ch[num]+"")) continue; sb.append(ch[num]); } System.out.println(sb); //再比较字符串与输入的是否相等。相等就跳出循环,不等就刷新验证码。 String code=sc.nextLine(); if(sb.toString().equals(code)){ System.out.println("验证码正确!"); break; }else{ System.out.println("验证码错误!"); } } }}
3、去掉ArrayList中的重复元素
要求ArrayList集合中对象重写equals()方法。
/*方法中返回一个新集合替换老集合*/public static ArrayList method(ArrayList oldArray){//定义新集合,存储去掉重复元素后的集合 ArrayList newArray=new ArrayList(); Iterator it=oldArray.iterator(); while(it.hasNext()){ Object obj=it.next(); if(!newArray.contains(obj)) newArray.add(obj); } return newArray; }contains()会调用equals();对象中重写的equals():public boolean void equals(Object obj){ if(this==obj) return true; if(obj==null) return false; if(obj instanceof Person){ Person p=(Person)obj; return this.name.equals(p.name)&&this.age==p.age; }}
Vector类
没有集合框架以前,存储对象只能依赖Vector。Vector底层的数据结构也是可变数组,
但是它线程安全,运行效率慢,而且每次数组扩容是100%。所以被ArrayList取代了。
LinkedList类
1、特点:
底层数据结构是链表实现,以记录对象地址的方式存储
查询慢,增删快
线程不安全,执行效率高
2、LinkedList类特有的方法
addFirst()将元素添加到链表的开头
addLast()将元素添加到链表的结尾
Object getFirst()获取链表的开头
Object getLast()获取链表的结尾
Object removeFirst()移除链表的开头,返回被移除前的对象
Object removeLast()移除链表的结尾, 返回被移除前的对象
JDK1.6开始,有n个新的方法,把以前的方法取代,不做掌握
offerFirst()>addLast()
peekFirst()>getLast()
pollFirst()>removeLast()
3、LinkedList模拟堆栈和队列,体现的是思想的转换:将jdk中的方法封装起来,让用户调用我们自己的方法来执行程序,而不是
用jdk中的方法。
import java.util.*;class MyMethod{ private LinkedList link=null; public MyMethod(){ link=new LinkedList();} public void add(Object obj){ link.add(obj); } //先进先出 public Object get(){ return link.removeFirst(); } //先进后出 public Object get(int x){ return link.removeLast(); } public boolean isEmpty(){ return !link.isEmpty(); } }public class LinkedListOut{ public static void main(String[] args) { MyMethod my=new MyMethod(); my.add("2"); my.add("3"); my.add("4"); while(my.isEmpty()){ System.out.println(my.get(2)); } }}
Set 接口
1、Set集合派系的特点:
不允许存储重复元素
没有下标
无序的集合,存储的和取出的顺序不一样
2、Set接口中的方法和Collection接口中的一致
HashSet类
1、HashSet类除有和Set接口一样的特点外,自身的特点是:
底层数据结构:哈希表,存储的是对象的哈希值
HashSet本身没有功能,功能来自于HashMap。HashSet调用HashMap的功能。
2、HashSet的存储原理:
存储对象时,先调用对象的hashcode(),获取该对象的哈希值(十进制数)。HashSet看
存储过这个哈希值没。如果没有就将这个哈希值存到自己的哈希表中;如果有,就再
调用对象的equals()。如果相等就不存到HashSet集合中,不等就挂载到HashSet中原
有的哈希值下面。
3、HashSet集合取出重复的元素,保证对象的唯一性
需重写hashCode()和equals():
public int hashCode(){ //利用name变量 age变量 return name.hashCode()+age*13; } //同名的,和同年龄的对象,返回真 public boolean equals(Object obj){ if( this == obj) return true; if(obj == null) return false; if(obj instanceof Person){ Person p = (Person)obj; return this.name.equals(p.name) && this.age == p.age; } return false; }
4、hashCode和equals的问题
如果两个对象具有相同的哈希值,两个对象进行equals比较,不一定返回真;
如果两个对象进行equals比较,返回真,两个对象具有相同的哈希值吗,必须相同。
LinkedHashSet类---HashSet的子类
LinkedListSet独有的特点:
有序的,基于哈希表的链表,线程不安全,执行效率高
TreeSet类
1、TreeSet独有的特点:
底层结构是二叉树;
对存储的对象自然排序;
线程不安全,执行效率高。
2、将自定义的对象Person存储到TreeSet集合,出现了类型转换异常
ClassCastException :原因,Person不能被转成java.lang.Comparable
TreeSet中,需要对Person进行排序,可是你的Person对象,不具备自然顺序。
3、TreeSet集合中使对象具备自然顺序:
自定义的类需实现Comparable接口,重写compareTo()方法;
或者,自定义比较器类,实现接口Comparator,重写Compare(Object o1,Object o2)。
1 方法一: 2 //主要比较姓名,如果姓名一样,比较年龄。也可以主要比较年龄 3 public int compareTo(Object obj){ 4 //this 后进来的, obj先进来的对象 5 Person p = (Person)obj; 6 int num = this.name.compareTo(p.name); 7 return num == 0 ? this.age - p.age : num; 8 } 9 方法二:10 /*11 * 自定义比较器,对于TreeSet集合中存储的Person对象进行排序12 */13 Set set=new TreeSet(new Comparator());14 public class MyComparator implements java.util.Comparator{15 //重写compare方法,做什么事情,比较Person对象的年龄和姓名16 // ts.add(new Person("lisi",20));17 // ts.add(new Person("zisi",21));18 public int compare(Object o1, Object o2){19 //o1 和 o2 比较一下,年龄,主要条件,年龄不同,不需要比较姓名20 Person p1 = (Person)o1;21 Person p2 = (Person)o2;22 //Person的变量,只能用get方法获取23 int num = p1.getAge() - p2.getAge();24 return num == 0? p1.getName().compareTo(p2.getName()) : num;25 }26 27 }
注:两个方法都是大于0,就存后面;等于0,就不存;小于0,存前面。
泛型
1、格式:
集合类<数据类型> 变量 = new 集合类<数据类型>();
数据类型,指定了这个集合,只能存储这个类型的对象。
如: ArrayList<E> boolean add(E e) E:看成是一个变量
ArrayList<String> 所有的E都变成了String
注意:存储基本数据类型,泛型必须写对应的包装类
2、泛型的好处:
泛型的出现,将安全问题,由运行时期,提前到了编译时期
减少了代码,使用泛型避免类型的强制转换
3、自定义的泛型,保证数据安全
泛型类,声明类的时候,加上泛型
泛型方法, 在方法上定义泛型,和类无关,需声明;
public<T> void method(T t){}
public static<T> void method(){}
注意: 静态的方法的泛型不要和类上的一样。因为
静态的成员是优先于对象存在,用类名调用时无法指定
泛型的类型。
方法中的泛型是局部的泛型,不影响类名的泛型。
泛型接口,在接口名后面加上
通常定义实现类,实现接口的时候,不指定泛型,等待new实现类对象
的时候指定。
class MyComparator <T> implements Comparator<T>(){}
4、泛型的通配符和限定
上限限定 ? extends E 传递E类型,E的任意子类类型
下限限定 ? super E 传递E类型,E的任意父类类型
弊端:使用通配符后,不能强转。
Map接口
1、Map接口的特点:
一个键最多只能映射一个值
不允许出现重复的键
2、Map接口的方法:
V put(K,V) 将键值对存储到集合。返回值,存储了重复的键,返回被覆盖之前的值
V get(K) 根据键获取值,传递一个键,返回键映射的值,没有这个键,返回null
Set<K> keySet()键存储到Set集合
Set<Map.Entry<K,V>> entrySet()2014/8/15映射关系对象保存到Set集合
V remove(K)移除指定的键对应的值,返回被移除前的值,没有移除成功,返回null
boolean containsKey(K)判断集合中,有没有这个键,有返回true
boolean containsValue(V)判断集合中个,有没有这个值,有返回true
Collection values()将集合中的所有值,保存到Collection集合
3、获取Map集合中键值对的方式?
第一种,利用Map中的一个方法keySet(),Map集合中的键,存储到Set集合
迭代Set集合,获取出来的是所有的键
通过键获取值,Map接口中的get方法
1 Map<String,Integer> map=new HaspMap<String,Integer>();2 Set<String> set=map.keySet();3 Iterator<String> it=set.iterator();4 while(it.hasNext()){5 String key=it.next();6 Integer value=http://www.mamicode.com/map.get(key);7 System.out.println(key+"..."+value);8 }
第二种利用Map集合中的键值对映射关系获取
Map接口中有一个方法entrySet(),获取键值对的映射关系对象Entry,将这个对象Entry存储到了Set集合
迭代Set集合,获取出来的Set集合中存储的是映射关系对象Entry
通过映射关系对象中的方法 getKey getValue
1 Map<String,Integer> map=new HasMap<String,Integer>();2 Set<Map.Entry<String,Integer>> set=map.entrySet();3 Iterator <Map.Entry<String,Integer>> it=map.iterator();4 while(it.hasNext()){5 Map.Entry<String,Integer> me=it.next();6 String key=me.getKey();7 Integer value=http://www.mamicode.com/me.getValue();8 System.out.println(key+"..."+value);9 }
HashMap类
1、HashMap独有的特点
底层结构是哈希表,允许存储null值,null键
不同步,线程不安全,执行效率高
2、保证存储到的HashMap中的键唯一性。自定义对象保证唯一性,自定义对象需重写hoseCode()和equals()。
LinkedHashMap类-------HashMap的子类
1、特点:
底层结构是基于哈希表的链表实现的
键怎么存,怎么取
不同步,线程不安全,执行效率高
TreeMap类
1、TreeMap的特点:
底层数据结构是红黑树,记自然平衡二叉树
对键进行自然的排序,要求作为键的对象,具备自然排序或者自定义比较器
不同步,线程不安全,执行效率高
2、TreeMap使用方式和TreeSet是一致的,保证对象的自然顺序,或者定义比较器就可以了。在存储的时候,Set使用add,Map使用put。
HashTable类
1、HashTable的特点:
底层数据结构式哈希表
线程安全,不允许存null值,null键
因为HashTable出了线程和null的区别,其他的和HashMap的一致,所以被HashMap取代了。但是HashTable的一个子类Properties至今
很活跃。
Properties类---HashTable的子类
1、Properties的特点:
线程安全,与IO流配合使用。这个集合的泛型已经定好了,键值的泛型都是String。
2、Properties的两个特有的方法:
setProperty(String key,String value)
getProperty(String key)
3、计算一个字符串中每个字符出现的次数:
1 import java.util.*; 2 public class TreeMapTest { 3 public static void main(String[] args) { 4 String s = "asrrfegrr4gw34g"; 5 //String s = "aa"; 6 //建立TreeMap集合,键就是单个字符,值出现次数 7 TreeMap<Character,Integer> tm = new TreeMap<Character, Integer>(); 8 //将字符串变成字符数组,操作单个字符 9 char[] ch = s.toCharArray();10 for(int x = 0 ; x < ch.length ; x++){11 //遍历到每一个字符,字符当作键,到集合中获取值12 Integer i = tm.get(ch[x]);13 if(i==null){14 //说明键,不存在的,字符没有出现过,把字符当作键,值为1存储到集合15 tm.put(ch[x], 1);16 }else{17 //说明键存在,将值++后存储会集合18 19 tm.put(ch[x], ++i);20 }21 }22 System.out.println(tm);23 }24 }
JDK1.5后的新特性
1、增强for循环:只适合遍历数组和集合
格式:
for(数据类型 变量:集合或者数组){
输出(变量);
}
2、可变参数格式:
方法名(数据类型...变量名)
注意事项:
可变参数的数据类型唯一
多个参数,可变参数放最后
可变参数只能写一个
Collections集合工具类<List专用>
1、凡是工具类里面的成员都是静态的
2、集合工具类的方法:
static sort(List list)可以排序List集合,升序
static sort(List list,Comparator com)传递List,比较器,按照比较器对集合升序排序
static Comparator reverseOrder()返回一个比较器,逆转了对象的自然顺序(Comparable)
static Comparator reverseOrder(传递比较器)返回比较器,逆转了传递的比较器的顺序
static int binarySerach(List list,Object key)集合的折半查找,如果找不到该元素,返回-插入点-1。插入点,就是将查 找的元素,放在集合中个,保证集合有序, 下标插入点
static reverse(List list)反转集合
static fill(List list,Object o)替换集合中的所有对象
static shuffle(List list)随机排列集合中的对象,洗牌
Collections工具类中有一组方法 synchronized开头的,将线程不安全的集合,变成线程安全的集合
Arrays数组工具类
数组工具类中的方法:
static sort(数组) 数组进行升序排序,快排
static int binarySerach(数组,查找的元素)数组的折半查找
static String toString(数组)不是重写Object,将数组变成字符串
static List asList(数组)数组转集合,List集合,集合长度不可变
2Collection接口方法 T[] toArray(T[] a) 集合变数组