Java常用类库(2)——集合
1.类集可以称为是Java对数据结构的充分实现
2.类集的分类:Collection Map Iterator 是最大的接口
3.Collection是单值集合,她又被细分为两个接口list 和 set。
4.Collection中常见的方法:
1 public static void main(String [] args){ 2 //创建一个Collection对象,采用多态的的形式 3 Collection cl = new ArrayList<Interger>(); 4 //向集合中添加元素 5 cl.add(18); 6 cl.add(19); 7 //创建一个Vector对象 8 Vector<Interger> v = new Vector<Interger>(); 9 //向里面添加元素 10 v.add(13); 11 v.add(14); 12 //像最上面的集合中添加一个集合 13 cl.addAll(v); 14 //查找一个元素是否存在 15 System.out.print(cl.contains(12));//false 16 //判断集合是否为空 17 System.out.print(cl.isEmpty());//false 18 //从集合中删除一个对象remove(Object o); 19 //从集合中删除一组对象remove(Collection<?>c); 20 //判断集合没有指定的集合retainsAll(Collection<?>c); 21 //求出元素的个数 22 int num = cl.size(); 23 //以对象的数组形式返回集合的所有内容 24 Interger in = cl.toArray(); 25 //equals(Object o);从Object 类中覆写而来 26 //hashCode(Object o);从Object类中覆写而来 27 }
4.list允许重复,set不允许重复。
5.list中常见的方法:
1 public static void main(String [] args){ 2 //通过多态创建集合对象 3 List list = new ArrayList<Interger>(); 4 //在指定位置添加元素 5 list.add(0,e); 6 list.add(1,e2); 7 list.add(2,e3); 8 //add(int index,Collection<?>c)在指定位置添加一组元素 9 //根据索引位置取出一组元素 10 cl.get(0); 11 //删除指定位置处的元素 12 cl.remove(1); 13 //修改指定位置处的内容 14 cl.set(2,e); 15 //截取集合,返回子集合 16 cl.subList(1,3); 17 }
5.list的实现类:ArrayList Vector LinkedList
5.1)ArrayList类:
1)ArrayList:底层是动态的数组,具有查找,修改块,增加删除慢的特点
2)ArrayList中构造方法:
ArrayList() |
构造一个初始容量为10的空列表。
|
---|---|
ArrayList(int initialCapacity) |
构造具有指定初始容量的空列表。
|
ArrayList(Collection<? extends E> c) |
按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。
|
3)ArrayList中常见的方法:
4)相应的代码操作如上
5.2)Vector类:Vector类似于ArrayList,但是该类是线程安全的,她的操作类似于Vector.
5.3) LinkedList类:
1)她的底层是双向链表,可以当作是栈和队列(单向队列,双端队列)来使用。
2)构造方法:
3)常用方法:
4)代码的演示如上
6.set集合:
1.set继承自Collection ,所以很多方法都和Collection相同。
2.Collection 和 set集合都没有提供获取元素的代码,那set集合如何获取呢
2.1)通过迭代器的方式获取
2.2)通过toArray()方法转换为数组的方式获取
3.set集合不包含重复元素
4.set集合不适合用来存储可变对象(例如person类等),因为set集合的一些子类是无序的,当修改一些值的时候,会导致内存空间错乱
5.set集合接口的实现类:hashSet TreeSet
5.1)hashSet集合:
1)底层是散列表(哈希表),她的存储是无序的
2)他的使用方法同set相似
5.2)TreeSet集合:
1)底层的数据结构是二叉树,是有序存储
2)迭代器快速失败:带迭代器在迭代一个集合时,有另一个线程改变了集合的长度,导致于迭代器预期的长度不同,此时会报错
迭代器安全失败:迭代器迭代的是复制的子集合
3)TreeSet集合是有序打印的:
1 public static void main(String[] args) { 2 //创建TreeSet集合 3 TreeSet<String> data = new TreeSet<String>(); 4 data.add("D"); 5 data.add("C"); 6 data.add("A"); 7 data.add("B"); 8 for(String s:data){ 9 System.out.println(s); 10 } 11 } 12 //结果为:A B C D
4)当TreeSet在比较自定义类型的对象时,系统不认识,要报错,这个时候我们就要让该类型实现Comparable接口,并且从写里面的CompareTo()方法
1 public static void main(String[] args) { 2 //创建TreeSet集合 3 TreeSet<Person> data = new TreeSet<Person>(); 4 Person p1 = new Person("张三",18); 5 Person p2 = new Person("李四",19); 6 Person p3 = new Person("王五",20); 7 //如果两个对象的年龄相同,因为不重复,后者重复的年龄添加失败 8 data.add(p1); 9 data.add(p2); 10 data.add(p3); 11 12 for(Person s:data){ 13 System.out.println(s); 14 } 15 } 16 //静态内部类 17 static class Person implements Comparable<Person>{ 18 19 private String name; 20 private int age; 21 22 @Override 23 public int compareTo(Person o) { 24 //当this小时返回正数,this大返回负数,两个相同时返回0 25 //此时以年龄来比较 26 if(this.age>o.age){ 27 return -1; 28 }else if(this.age<o.age){ 29 return 1; 30 } 31 return 0; 32 } 33 34 public Person(String name, int age) { 35 this.name = name; 36 this.age = age; 37 } 38 39 public String getName() { 40 return name; 41 } 42 43 public void setName(String name) { 44 this.name = name; 45 } 46 47 public int getAge() { 48 return age; 49 } 50 51 public void setAge(int age) { 52 this.age = age; 53 } 54 55 @Override 56 public String toString() { 57 return "Person{" + 58 "name='" + name + '\'' + 59 ", age=" + age + 60 '}'; 61 } 62 63 @Override 64 public boolean equals(Object o) { 65 if (this == o) return true; 66 if (o == null || getClass() != o.getClass()) return false; 67 Person person = (Person) o; 68 return age == person.age && 69 Objects.equals(name, person.name); 70 } 71 72 @Override 73 public int hashCode() { 74 return Objects.hash(name, age); 75 } 76 77 }
6.Map集合:
1.Map接口是存储键值对的,
2.set接口的底层原理也是Map,他只存储一个值,另一个设置了默认值,因为键是不可以重复的,所以set集合不能重复
3.set接口中常用的方法:
1)clear();清空集合
2)keySet();获取Map集合的键,返回一个单独存储键的Set集合
3)get(键);通过迭代获取的键来寻找相应的值
4)put(K key,V value);添加元素,一个键只能对应一个值,当这个键原来有值是,新的值就将会覆盖原来的值,并且返回原来的值,原来没有值就会返回null
4)remove(K key);删除时会返回被删除的值V,这个场景可以用在既要取数据,又要删除的数据的操作中
5)containsKey(Object key);判断键在集合中是否存在
6)containsValue(Object value);判断值在集合中是否存在
7)replace(K key,V value);通过键把原来的值修改掉
8)size();返回键值对的对数
9)values();该方法值获取值,将他返回一个Collection类型的集合
4.Map接口的实现类:hashMap TreeMap LinkedMap hashTable等
1)hashMap:
1.1)是基于哈希表的Map接口的实现
1.2)哈希表的结构:
解释:1.Java中万物皆对象,通过对象调用hashCode()方法得到每个对象的哈希值,将这个对16进行取余,得到一个0-15的数字,将这个数字作为下表,存放到哈希 数组里面,数组里面是链表,可以存放多个余数相同的对象
2.哈希表是数组+链表的形式,hashMap集合的默认有16个长度,下表从0到15.,每个空间成为一个哈希桶
3.哈希桶的默认值为16,当大于散列因子(0.75),即哈希桶的数据存入数量大于桶总数的百分之七十五的时候,会进行自动扩容,长度为原来的两倍,取余长 度也会相应的改变。
1.3)hashMap集合的put(K key , V value)方法:
源码:
2)Map集合的一些常用操作,几乎所有的都是这一套操作流程:
1 //创建集合对象 2 HashMap<String ,String> map = new HashMap<>(); 3 //存入值 4 map.put("key1","锄禾日当午"); 5 map.put("key2","汗滴禾下土"); 6 map.put("key3","谁知盘中餐"); 7 map.put("key4","粒粒皆辛苦"); 8 //调用方法来获取键的集合 9 Set<String> ss = map.keySet(); 10 for(String s:ss){ 11 System.out.println(s+"->"+map.get(s)); 12 } 13 14 } 15 //上诉的HashMap可以变换为Map的其他实现类
3)Map集合中不同实现类的差别:
hashTable采用的是排队
TreeMap是有顺序的,根据键排队底层是二叉树
LinkedHashMap与HashMap相似,他弥补了HashMap无序的不住,他底层是哈希表和双向链表,既保证了有序,又保证了查找等操作快
5.散列表散列操作
1)HashMap集合的哈希表的初始值要根据实际情况进行合理的赋值,来避免频繁的散列(哈希表的重建)带来的不必要的繁琐的操做
2)散列因子越大,空间利用率越好,查询效果越慢;散列因子越小,空间利用率越小,查询效果越好
6.存储自定义对象
1)代码演示:
1 public static void main(String[] args) { 2 //当用Map集合存储一个对象的时候,一定要重写hashCode方法,不然底层默认的是Object类的hashCode方法 3 //创建集合对象 4 HashMap<Book,String> map = new HashMap<>(); 5 //创建Book对象 6 Book b1 = new Book("金苹果","讲诉了种植苹果树的艰辛过程"); 7 //添加元素 8 map.put(b1,"我们人生的第一本书"); 9 //创建Book对象 10 Book b2 = new Book("银苹果","讲诉了种植苹果树的艰辛过程"); 11 //添加元素 12 map.put(b1,"我们人生的第二本书"); 13 //此时修改b1的属性 14 b1.setName("铜苹果");//将属性改变后,从新计算了一个哈希值,这样通过这个哈希值,我们找不到原来存储的对象了 15 //散列,即重新创建一个数据结构,将原来的数据重新储存到新的哈希表中 16 //此时通过获取值,返回值为null 17 System.out.println(map.get(b1)); 18 //创建Book对象 19 Book b3 = new Book("金苹果","讲诉了种植苹果树的艰辛过程"); 20 //添加元素 21 map.put(b3,"我们人生的第二本书"); 22 //当b3对象和b1内容相同的时候,会去通过equals判断两个对象是否相同,如果相同,则会找到原来的哈希值,找到b1对象的数据 23 //因为是新创建的对象,所以equals比较为false,找不到换来的b1,所以不要改变存储在Map集合中的数据 24 System.out.println(b3); 25 26 27 } 28 //静态内部内 29 static class Book{ 30 31 private String name; 32 private String info; 33 34 public Book(String name, String info) { 35 this.name = name; 36 this.info = info; 37 } 38 39 public String getName() { 40 return name; 41 } 42 43 public void setName(String name) { 44 this.name = name; 45 } 46 47 public String getInfo() { 48 return info; 49 } 50 51 public void setInfo(String info) { 52 this.info = info; 53 } 54 55 @Override 56 public String toString() { 57 return "Book{" + 58 "name='" + name + '\'' + 59 ", info='" + info + '\'' + 60 '}'; 61 } 62 63 @Override 64 public boolean equals(Object o) { 65 if (this == o) return true; 66 if (o == null || getClass() != o.getClass()) return false; 67 Book book = (Book) o; 68 return Objects.equals(name, book.name) && 69 Objects.equals(info, book.info); 70 } 71 72 @Override 73 public int hashCode() { 74 return Objects.hash(name, info); 75 } 76 77 78 }
6.JDK9版本提供的集合新特性:
1)
(已上是Map的方法,List和set类似)
只有List set Map这三个接口才可以有这个特性,这个特性就是建立特定长度不可变的集合长度,主要用来存储数据少的情况下
2)代码演示:
1 public static void main(String[] args) { 2 //演示List 3 List<String> list = List.of("锄禾日当午", "汗滴禾下土", "谁知盘中餐", "粒粒皆辛苦"); 4 //该集合长度已经定了,不能在使用添加的操作 5 for(String s:list){ 6 System.out.println(s); 7 } 8 9 //演示set集合 10 Set<String> set = Set.of("锄禾日当午", "汗滴禾下土", "谁知盘中餐", "粒粒皆辛苦"); 11 for(String s:set){ 12 System.out.println(s); 13 } 14 15 //演示Map集合 16 Map<String, String> map = Map.of("老鼠", "爱大米", "我", "爱干饭"); 17 Set<String> strings = map.keySet(); 18 for(String str:strings){ 19 System.out.println(str+"->"+map.get(str)); 20 } 21 }
二:迭代器
1.迭代器有两种:Iterator(用于所有的Collection类集的迭代遍历)和ListIterator(仅用于List集合的遍历)
2.Iterator中的方法:
3.代码演示:
3.1)基本操作
1 public static void main(String[] args) { 2 //1.创建一个集合对象 3 ArrayList<Integer> list = new ArrayList<>(); 4 //向集合里面添加元素 5 list.add(1); 6 list.add(2); 7 list.add(3); 8 list.add(4); 9 list.add(5); 10 //调用方法,生成迭代器对象 11 Iterator<Integer> iterator = list.iterator(); 12 while(iterator.hasNext()){//判断指针后面是否还有元素,有就迭代 13 Integer next = iterator.next();//移动指针,并且获取当前指针的值 14 System.out.println(next); 15 } 16 }
3.2)演示remove()方法
1 public static void main(String[] args) { 2 //1.创建一个集合对象 3 ArrayList<Integer> list = new ArrayList<>(); 4 //向集合里面添加元素 5 list.add(1); 6 list.add(2); 7 list.add(3); 8 list.add(4); 9 list.add(5); 10 //调用方法创建迭代器 11 Iterator<Integer> iterator = list.iterator(); 12 //先将指针移到指定位置后才可以删除 13 iterator.next(); 14 iterator.remove(); 15 System.out.println(list.size());//4 16 }
添加的元素添加到next的上一个位置,如图中所示,上诉图是迭代器的工作原理
4.ListIterator中的方法:
4.1)代码演示:
public static void main(String[] args) { //1.创建一个集合对象 ArrayList<Integer> list = new ArrayList<>(); //向集合里面添加元素 list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); //调用方法创建迭代器 ListIterator<Integer> iterator = list.listIterator(); //添加元素 iterator.add(100); //将指针往下移动 iterator.next(); iterator.next(); //将第二个元素设置为200 iterator.set(200); //将指针上移动,遍历整个集合 iterator.previous(); iterator.previous(); //以为添加了一个100,所以还要上移动 iterator.previous(); //遍历 while(iterator.hasNext()){ System.out.println(iterator.next());; } } /* 结果: 100 1 200 3 4 5 */
三:forEach循环(增强for循环)
1.作用范围:数组+Collection集合
2.代码演示:
1 public static void main(String[] args) { 2 //1.创建一个集合对象 3 ArrayList<Integer> list = new ArrayList<>(); 4 //向集合里面添加元素 5 list.add(1); 6 list.add(2); 7 list.add(3); 8 list.add(4); 9 list.add(5); 10 //调用方法创建迭代器 11 ListIterator<Integer> iterator = list.listIterator(); 12 //添加元素 13 iterator.add(100); 14 //将指针往下移动 15 iterator.next(); 16 iterator.next(); 17 //将第二个元素设置为200 18 iterator.set(200); 19 //将指针上移动,遍历整个集合 20 iterator.previous(); 21 iterator.previous(); 22 //以为添加了一个100,所以还要上移动 23 iterator.previous(); 24 //遍历 25 // while(iterator.hasNext()){ 26 // System.out.println(iterator.next());; 27 // } 28 for(Integer s:list){ 29 System.out.println(s); 30 } 31 }
三:总结:
集合同数组一样,都是用来存数据的框框,当我们要存储数据时,通过考虑数据的特点以及他要的操作,选取合适的集合来存储数据,当在操作数据时,对于我们要解决的需求,我们因该考虑该集合中的方法,去查找API文档,如果能找到就直接用,不能找到在考虑其他方法。