集合框架--1、List、Set、Map

集合的继承实现关系: 
  
ConcurentHashMap
Vector和ArrayList区别
HashTable和HashMap的区别
Collecton接口常用的子接口有:List接口、Set接口
List接口常用的子类有:ArrayList类、LinkedList类
Set接口常用的子类有:HashSet类、LinkedHashSet类
List允许有重复的元素,有序。
Set不予许有重复元素,无序。
Collection接口概述
既然Collection接口是集合中的 顶层接口,那么它中定义的所有功能子类都可以使用, 是共性的功能 ,是集合中所有实现类必须拥有的方法。
所有我们学习的时候首先从父类Collection开始,然后再学习子类中特有的内容。
Collection接口的基本方法
创建集合的格式:
方式1:Collection<元素类型> 变量名 = new ArrayList<元素类型>();
方式2:Collection 变量名 = new ArrayList();
方式1创建的集合,只能存储<>中指定的元素类型,该方式为常用方式
方式2创建的集合,集合的元素类型默认为Object类型,即任何类型的元素都可以存储。
演示Collection接口中的方法:
public void test1() {
   Collection<String> collection = new ArrayList<>();
    collection.add( "张三");
    collection.add( "李四");
    collection.add( "王五");
    collection.add( "张三");
   System. out.println( collection);
    collection.remove( "张三");
   System. out.println( collection);
   Object[] array = collection.toArray();
    for ( int i = 0; i < array. length; i++) {
     System. out.print( array[ i] + "  ");
   }
}
[张三, 李四, 王五, 张三]
[李四, 王五, 张三]
李四  王五  张三  


Iterator迭代器
java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。Iterator接口的常用方法如下:
hasNext()方法:用来判断集合中是否有下一个元素可以迭代。如果返回true,说明可以迭代。
next()方法:用来返回迭代的下一个元素,并把指针向后移动一位。
在Collection接口描述了一个抽象方法iterator方法,所有Collection子类都实现了这个方法,并且有自己的迭代形式。
public void test2() {
   Collection<String> collection = new ArrayList<>();
    collection.add( "张三");
    collection.add( "李四");
    collection.add( "王五");
    collection.add( "张三");
   Iterator<String> iterator = collection.iterator();
    while ( iterator.hasNext()) { //判断有没有下一个,有下一个返回true
     String data = iterator.next(); //用来返回迭代的下一个元素,并把指针向后移动一位
     System. out.println( data);
   }
}


List接口特点(也是所有实现类的特点):
1、它是一个元素存取 有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
2、它是一个带有 索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
3、 集合中可以有 重复的元素,通过元素的equals方法,来比较是否为重复的元素。
List接口的常用子类有:
ArrayList集合
LinkedList集合
ArrayList:
1、增加元素方法
     add(Object e):向集合末尾处,添加指定的元素
     add(int index, Object e):向集合指定索引处,添加指定的元素,原有元素依次后移
2、删除元素删除
     remove(Object e):将指定元素对象,从集合中删除,返回值为被删除的元素
     remove(int index):将指定索引处的元素,从集合中删除,返回值为被删除的元素
3、替换元素方法
     set(int index, Object e):将指定索引处的元素,替换成指定的元素,返回值为替换前的元素
4、查询元素方法
     get(int index):获取指定索引处的元素,并返回该元素
public static void main(String[] args) {
   List<String> list = new ArrayList<String>();
    list.add( "abc1");
    list.add( "abc2");
    list.add( "abc3");
    list.add( "abc4");
   
    list.add(2, "Java"); //List接口定义的方法
   Iterator<String> iterator = list.iterator();
    while ( iterator.hasNext()) {
     String str = iterator.next();
     System. out.println( str);
   }
   System. out.println( "--------------");
    //由于List集合拥有索引,因此List集合迭代方式除了使用迭代器之外,还可以使用索引进行迭代。
    for ( int i = 0; i < list.size(); i++) {
     String str = list.get( i);
     System. out.println( str);      
   }
   System. out.println( "--------------");
   //迭代器的并发修改异常 java.util.ConcurrentModificationException
   //就是在遍历的过程中,使用了集合方法修改了集合的长度,不允许的
    //对集合使用迭代器进行获取,获取时候判断集合中是否存在 "abc3"对象
    //如果有,添加一个元素 "ABC3"
   Iterator<String> it = list.iterator();
    while( it.hasNext()){
     String s = it.next();
      //对获取出的元素s,进行判断,是不是有"abc3"
      if( s.equals( "abc3")){
         list.add( "ABC3");
     }
     System. out.println( s);
   }
}
运行上述代码发生了错误 java.util. ConcurrentModificationException这是什么原因呢?
在迭代过程中,使用了集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性。
并发修改异常解决办法: 在迭代时,不要使用集合的方法操作元素。
那么想要在迭代时对元素操作咋办?通过ListIterator迭代器操作元素是可以的,ListIterator的出现,解决了使用Iterator迭代过程中可能会发生的错误情况。
public void ttt() {
   List<String> list = new ArrayList<String>();
   list.add( "1");
   list.add( "2");
   list.add( "3");
   list.add( "4");
   list.add( "5");
   for (String item : list) {
     if ( item.equals( "3")) {
        System. out.println( item);
        list.remove( item);
     }
   }
   System. out.println( list.size());
}
遍历的时候移除使用Iterator里面的remove方法。
Iterator iterator = list.iterator();
while ( iterator.hasNext()) {
   String data = (String) iterator.next();
    if ( data.equals( "3")) {
      // 不管在 foreach 还是Iterator遍历过程中都不允许使用list改变长度
       // 如果是移除元素,可以使用Iterator里面的remove,但是对于添加
       // Iterator就没有提供,可以参考ListIterator
      iterator.remove();
     System. out.println( data);
   }
}
System. out.println( list.size());
LinkedList:
LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。实际开发中对一个集合元素的添加与删除经常涉及到 首尾操作,而LinkedList提供了大量首尾操作的方法.
LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。
public void test() {
   LinkedList<String> link = new LinkedList<String>();
    //添加元素
    link.addFirst( "abc1");
    link.addFirst( "abc2");
    link.addFirst( "abc3");
    //获取元素
   System. out.println( link.getFirst());
   System. out.println( link.getLast());
    //删除元素
   System. out.println( link.removeFirst());
   System. out.println( link.removeLast());
   
    while(! link.isEmpty()){ //判断集合是否为空
     System. out.println( link.pop()); //弹出集合中的栈顶元素
   }
}
Vector:
Vector集合数据存储的结构是数组结构,为JDK中最早提供的集合。Vector中提供了一个独特的取出方式,就是枚举Enumeration,它其实就是早期的迭代器。此接口Enumeration的功能与 Iterator 接口的功能是类似的。 Vector集合已被ArrayList替代。枚举Enumeration已被迭代器Iterator替代。
Vector & ArrayList比较
相同点:Vector与ArrayList本质上都是一个Object[] 数组
不同点:
1)Vector是线程安全的集合类,ArrayList并不是线程安全的类。Vector类对集合的元素操作时都加了synchronized,保证线程安全。
2)Vector与ArrayList的扩容并不一样,Vector默认扩容是增长一倍的容量,Arraylist是增长50%的容量。ArrayList就有利于节约内存空间。
Vector无论查询还是增删都很慢,所以被ArrayList替代了。


Set接口:
Set中元素 无序不重复
HashSet:
无序:HashSet集合不能保证的迭代顺序与元素存储顺序相同。
不重复:HashSet集合,采用哈希表结构存储数据,保证元素唯一性的方式依赖于两个方法: hashCode()与equals()方法
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
HashSet元素不能重复,如何实现:

哈希表_百度百科

当向哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是Object类中的hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。即就是在给哈希表中存放对象时,会调用对象的hashCode方法,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为 哈希冲突,这时会调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回的是 true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。
总结:保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
给HashSet中存储JavaAPI中提供的类型元素时,不需要重写元素的hashCode和equals方法,因为这两个方法, 在JavaAPI的每个类中已经重写完毕,如String类、Integer类等。
public void test551() {
  // 创建HashSet对象
   Set<String> set = new HashSet<String>();
   // 给集合中添加自定义对象
   set.add( "zhangsan");
   set.add( "lisi");
   set.add( "wangwu");
   set.add( "zhangsan");
   set.add( new String( "lisi"));
   for (String string : set) {
      System. out.println( string);
   }
}
lisi
zhangsan
wangwu
因为HashSet默认是根据变量的地址判断是不是已经加入HashSet了。
这样的话:
Student  stu1 = new Student(1, "张三", 20);
Student  stu2 = new Student(1, "张三", 20);
stu1和stu2都能够加入HashSet里面,因为他们的地址不一样,但是这其实是同一个Student,
只是分别通过不同的方式new出来,如果stu1和stu2都能够加入的话,那么这个学生就在HashSet里面重复了。
所有的类都继承自Object类,Object类里面有hashCode和equal这两个方法,
HashSet调用add方法时候判断这个对象有没有已经在Set里面就是根据equal方法
解决方法就是:我们自己来判断是不是已经存在,选择你要比较的属性,覆写hashCode和equal方法
自定义类型(如果要使用HashSet,必须手动实现hashCode和equals):
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    public Student() {
      super();
      // TODO Auto-generated constructor stub
   }
    public Student(Integer id, String name, Integer age) {
      this. id = id;
      this. name = name;
      this. age = age;
   }
    public Integer getId() {
      return id;
   }
    public void setId(Integer id) {
      this. id = id;
   }
    public String getName() {
      return name;
   }
    public void setName(String name) {
      this. name = name;
   }
    public Integer getAge() {
      return age;
   }
    public void setAge(Integer age) {
      this. age = age;
   }
    @Override
    public String toString() {
      return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
   }
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + (( age == null) ? 0 : age.hashCode());
      result = prime * result + (( id == null) ? 0 : id.hashCode());
      result = prime * result + (( name == null) ? 0 : name.hashCode());
      return result ;
   }
    @Override
    public boolean equals(Object obj) {
      if ( this == obj)
         return true;
      if ( obj == null)
         return false;
      if (getClass() != obj.getClass())
         return false;
     Student other = (Student) obj;
     // 根据id值和name值age值是不是相等看是否已经存在
      if ( age == null) {
         if ( other. age != null)
           return false;
     } else if (! age.equals( other. age))
         return false;
      if ( id == null) {
         if ( other. id != null)
           return false;
     } else if (! id.equals( other. id))
         return false;
      if ( name == null) {
         if ( other. name != null)
           return false;
     } else if (! name.equals( other. name))
         return false;
      return true;
   }
}
测试:
public void test66() {
   HashSet<Student> set = new HashSet<>();
    set.add( new Student(1, "zhangsan1", 21));
    set.add( new Student(2, "zhangsan2", 22));
    set.add( new Student(3, "zhangsan3", 23));
    set.add( new Student(4, "zhangsan4", 24));
    set.add( new Student(2, "zhangsan2", 22));
   Iterator<Student> iterator = set.iterator();
    while ( iterator.hasNext()) {
     Student student = iterator.next();
     System. out.println( student);
   }
}
结果:
Student [id=3, name=zhangsan3,  age=23]
Student [id=4, name=zhangsan4,  age=24]
Student [id=1, name=zhangsan1,  age=21]
Student [id=2, name=zhangsan2,  age=22]
LinkedHashSet介绍
我们知道HashSet保证元素唯一,可是元素存放进去是 没有顺序的,那么我们要保证有序?
在HashSet下面有一个子类LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。
public void test4() {
   Set<String> set = new LinkedHashSet<String>();
    set.add( "bbb");
    set.add( "aaa");
    set.add( "abc");
    set.add( "bbc");
    Iterator it = set.iterator();
    while ( it.hasNext()) {
     System. out.println( it.next());
   }
}
TreeSet:
Java中的TreeSet是Set的一个子类,TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一。
那TreeSet为什么能保证元素唯一,它是怎样排序的呢?
public void test5() {
   TreeSet<Student> treeSet = new TreeSet<>();
    treeSet.add( new Student(1, "张三", 23));
    treeSet.add( new Student(2, "李四", 13));
    treeSet.add( new Student(3, "周七", 13));
    treeSet.add( new Student(4, "王五", 43));
    treeSet.add( new Student(5, "赵六", 33));
   
    System. out.println( treeSet);
}
执行结果:
出错,会抛出一个异常:java.lang.ClassCastException: com.situ.mall.test.Student cannot be cast to java.lang.Comparable
显然是出现了类型转换异常。原因在于我们需要告诉TreeSet如何来进行比较元素,如果不指定,就会抛出这个异常
如何解决:
如何指定比较的规则,需要在自定义类(Student)中实现```Comparable```接口,并重写接口中的compareTo方法
public class Student implements Comparable<Student>{
    private Integer id;
    private String name;
    private Integer age;
   
   //两个Student怎么比较大小
   @Override
    public int compareTo(Student o) {
        return 0;  //当compareTo方法返回0,元素值每次比较,都认为是相同的元素
        return 1;  //当compareTo方法返回值正数,元素值每次比较,都认为新插入的元素比上一个元素大,读取时就是正序排列的
        return -1; //当compareTo方法返回值负数,元素值每次比较,都认为新插入的元素比上一个元素小,读取时就是倒序排列的
   }
}
按照年龄来排序,年龄相同按照名字排序
public int compareTo( Student o) {
     int num = this. age - o. age;                //年龄是比较的主要条件
     return num == 0 ? this. name.compareTo( o. name) : num; //姓名是比较的次要条件
}
按照姓名长度排序:
public int compareTo( Student o) {
    int length = this. name.length() - o. name.length();               //比较长度为主要条件
    int num = length == 0 ? this. name.compareTo( o. name) : length;    //比较内容为次要条件
    return num == 0 ? this. age - o. age : num;                        //比较年龄为次要条件
}



Map接口
1、Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
2、Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
3、Collection中的集合称为单列集合,Map中的集合称为双列集合。
4、需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
5、Map中常用的集合为HashMap集合、LinkedHashMap集合。
Map的子类:HashMap、LinkedHashMap
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证 键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的 键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
Map接口中的常用方法
public static void main(String[] args) {
    // 创建Map对象
   Map<String, String> map = new HashMap<String, String>();
    // 给map中添加元素
    map.put( "星期一", "Monday");
    map.put( "星期日", "Sunday");
   System. out.println( map); // {星期日=Sunday, 星期一=Monday}
    // 根据指定的key获取对应的value
   String en = map.get( "星期日");
   System. out.println( en); // Sunday
    // 根据key删除元素,会返回key对应的value值
   String value = map.remove( "星期日");
   System. out.println( value); // Sunday
   System. out.println( map); // {星期一= Monday }
}
Map集合遍历键找值方式:
一、键找值方式:即通过元素中的键,获取键所对应的值
1.获取Map集合中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键
2.遍历键的Set集合,得到每一个键
3.根据键,获取键所对应的值
public void test1() {
    // 创建Map对象
   Map<String, String> map = new HashMap<String, String>();
    // 给map中添加元素
    map.put( "邓超", "孙俪");
    map.put( "李晨", "范冰冰");
    map.put( "刘德华", "柳岩");
    // 获取Map中的所有key
   Set<String> keySet = map.keySet();
    // 遍历存放所有key的Set集合
   Iterator<String> it = keySet.iterator();
    while ( it.hasNext()) {
      // 得到每一个key
     String key = it.next();
      // 通过key获取对应的value
     String value = map.get( key);
     System. out.println( key + "=" + value);
   }
}
二、Map集合遍历键值对方式
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
操作步骤与图解:
1.获取Map集合中, 所有的键值对(Entry)对象,以Set集合形式返回
2.遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象
3.通过键值对(Entry)对象,获取Entry对象中的键与值。
public void test2() {
    // 创建Map对象
   Map<String, String> map = new HashMap<String, String>();
    // 给map中添加元素
    map.put( "邓超", "孙俪");
    map.put( "李晨", "范冰冰");
    map.put( "刘德华", "柳岩");
    // 获取Map中的所有key与value的对应关系
   Set<Map.Entry<String, String>> entrySet = map.entrySet();
    // 遍历Set集合
   Iterator<Map.Entry<String, String>> it = entrySet.iterator();
    while ( it.hasNext()) {
      // 得到每一对对应关系
     Map.Entry<String, String> entry = it.next();
      // 通过每一对对应关系获取对应的key
     String key = entry.getKey();
      // 通过每一对对应关系获取对应的value
     String value = entry.getValue();
     System. out.println( key + "=" + value);
   }
}
注意:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。


Collections集合工具类

Collections类常用方法总结

Collections是集合工具类,用来对集合进行操作。部分方法如下:
public static <T> void sort(List<T> list) // 集合元素排序
//排序前元素list集合元素 [33,11,77,55]
Collections.sort( list );
//排序后元素list集合元素 [11,33,55,77]
public static void shuffle(List<?> list) //  集合元素存储位置打乱
//list集合元素 [11,33,55,77]
Collections.shuffle( list );
//使用shuffle方法后,集合中的元素为[77,33,11,55],每次执行该方法,集合中存储的元素位置都会随机打乱
排序操作:
void reverse(List list):反转
void shuffle(List list),随机排序
void sort(List list),按自然排序的升序排序
void sort(List list, Comparator c);定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j),交换两个索引位置的元素
void rotate(List list, int distance),旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。
查找,替换操作:
int binarySearch(List list, Object key), 对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll),根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c),根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj),用元素obj填充list中所有元素
int frequency(Collection c, Object o),统计元素出现次数
int indexOfSubList(List list, List target), 统计targe在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素。
Collections和Arrays
Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值