Java高级编程九:集合类 List Set Map及其子类用法

第九章 java集合

JCF集合框架
Collection单值类型集合:接口
Map键值对类型集合:接口
List有序不唯一
ArrayList
Vector
Stack
LinkedList
Set无序唯一
SortedSet有序唯一
HashSet
LinkedHashSet
HashMap
LinkedHashMap
TreeMap
Hashtable
Properties

1. Iterator迭代器

  • 如何创建迭代器

    Iterator<类型> i = list.iterator();
    
  • ​ 使用迭代器遍历集合

    for(Iterator<类型> i = list.iterator();i.hasNext();){
        
    }
    //for循环不通用
    //foreach遍历集合的同时删除集合里边的元素,会报错CME 并发修改异常
    //所以需要用迭代器自己的remove()方法
    
1.1迭代器方法
  1. 如何创建一个迭代器

    Iterator<泛型> car = list.iterator();
     //<>里边的泛型一定要和集合的泛型保持一致
    
  2. hasNext()

    判断集合是不是还有下一个元素、

  3. next()

    取出下一个元素

  4. remove()

    删除集合里边的元素

2. List

2.1 ArrayList
  1. 如何创建集合对象

    //jdk5.0以前 默认装Object[]
    ArrayList list = new Array();
    //jdk5.0之后 可以加泛型
    ArrayList<String> list = new ArrayList<String>();
    //7.0 后边的泛型可以省略
    Array<Integer> list = new ArrayList<>();
    
  2. 如何往集合添加元素

    //一次添加一个元素
    list.add(44);
    list.add(45);
    //一次添加多个元素    集合名字  元素
    Collections.addAll(list,22,22,33,4,5,5);
    
  3. 如何得到集合的大小

    list.size();
    
  4. 如何得到某一个元素

    list.get(下标);
    
  5. 判断集合是否包含一个元素

    list.contains(元素);
    
  6. 如何遍历集合

    for(int i = 0;i < list.size();i++){
        System.out.println(list.get(i));
    }
    for(Integer x: list){
        System.out.println(x);
    }
    
  7. 使用迭代器遍历集合

    //hadNext()判断迭代器后边是否还有元素
    for(Iterator<泛型> iter = list.iterator;car.hasNext();){
        //next()去除先一个元素
        car.next();
    }
    
2.1.1 ArrayList常用方法
  • remove()

    remove方法有两个,一个传下标,另外一个传元素,判断两个元素是不是一样的看元素底层的equals()方法。==具体看传入的参数的equals();==传入的参数会主动调用自己的equals()方法和集合里的每一个对象作比较

    //一次只可以删除一个对象
    
  • contains()

    比较集合是否包含指定元素也是用的equals

  • clear()

    清空集合用clear();

面试题

CollectionCollections 的区别

Collection 是所有单值类型集合的父接口 :interface

Collections 是集合的工具类 :class

2.1.2 ArrayList拓展🐴
  1. 集合扩容

    当创建一个数组对象的时候需要确定数组的大小

    ArrayList底层是基于Object[]数组实现的,集合里边存几个元素时根据ArrayList的构造方法决定的

    ①ArrayList list = new ArrayList(100);传入多少就开辟多少

    ②ArrayList list = new ArrayList();不传参数,底层默认开辟10块空间

    集合会自己扩容,所以不用担心不够用

    JDK6.0及以前 x * 3 / 2 + 1

    JDK7.0及以后 x + (x >> 1 )

    //把集合扩容到指定的空间
    list.ensureCapacity(300);
    //减少集合空间
    list.trimToSize();
    
  2. 手写集合

    public class Exec1{
        public static void main(String[] args){
    
    		AList list = new AList();
    		list.add("123");
    		list.add("456");
    		list.add(666);
    		System.out.println(list.size());
    		System.out.println(list.contains(new Integer(666)));
    		list.remove(0);
    		list.add("999");
    		System.out.println(list);
    		list.remove("999");
    		System.out.println(list);
    
    		//前后都要加泛型
    		AList<Integer> list1= new AList<>();
    		CollectionsTest.addAll(list1,123,123,234,543,7657);
    		System.out.println(list);
    
    		AList<Teacher> t = new AList<>();
    		t.add("赵信");
    		System.out.println(t);
    		t.remove("赵信");
    		System.out.println(t.size());
    
        }
    }
    class CollectionsTest{
    	public static void addAll(AList<Integer> list,Integer ... obj){
    		for(Integer data: obj){
    			list.add(data);
    		}
    	}
    }
    class AList<E>{
    	//数组用来存放元素
    	private Object[] data;
    	//元素个数
    	private int size;
    	//有参构造方法,用户传进来集合大小
    	public AList(int x){
    		if(x < 0){
    			System.out.println("ArrayIndexOutOfBoundsException:" + x);
    		}
    		data = new Object[x];
    	}
    	//无参构造方法,默认为10
    	public AList(){
    		this(10);
    	}
    	//得到集合大小
    	public int size(){
    		return size;
    	}
    	//得到元素
    	public Object get(int x){
    		return data[x];
    	}
    	//添加元素
    	public void add(Object obj){
    		//判断如果集合满了,就进行扩容
    		if(data.length == size){
    			Object[] temp = new Object[size + (size >> 1)];
    			System.arraycopy(data,0,temp,0,size);
    			data = temp;
    		}
    		data[size] = obj;
    		size++;
    	}
    	//删除元素,按照下标进行删除
    	public void remove(int x){
    		/*
    		  删除指定下标的元素,相当于把指定下标以后的元素复制到指定下标处,
    		  复制完成之后元素个数减一
    		*/
    		System.arraycopy(data,x + 1,data,x,size - x - 1);
    		size--;
    	}
    	//删除元素,按照指定元素删除
    	public void remove(Object obj){
    		for(int i = 0; i < size;i++){
    			//挨个遍历数组,找到一样的就删除吊
    			if(obj.equals(data[i])){
    				remove(i);
    			}
    		}
    	}
    	//判断集合里边是否包含指定元素
    	public boolean contains(Object obj){
    		if(obj == null) return false;
    		for(int i = 0;i < size;i++){
    			if(obj.equals(data[i])){
    				return true;
    			}
    		}
    		return false;
    	}
    	@Override
    	public String toString(){
    		String str = "[";
    		for(int i = 0; i < size -1;i++){
    			str = str + data[i] + ",";
    		}
    		return str + data[size - 1] + "]";
    	}
    }
    class Teacher{
    	String name
    	public Teacher(String name){
    		this.name = name;
    	}
    	@Override
    	public String toString(){
    		return name;
    	}
    }
    
  3. addAll()

    ArrayList<Integer> list = new ArrayList<>();
    ArrayList<Integer> test = new ArrayList<>();
    Collectoins.addAll(list,1,2,3,4,5,6);
    //直接把集合塞到另外一个集合里边
    test.addAll(list);
    
2.2 Vector
  • 语法和ArrayList一模一样

面试题

VectorArrayList的区别

  1. 同步线程不同

    Vector同一时间允许一个线程进行访问,效率较低但是不会出现并发错误

    ArrayList同一时间允许多个线程进行访问,效率较高,但是可能会出现并发错误

    从JDk5.0之后

  2. 扩容机制不同

    ArrayList:分版本

    ​ JDK6.0及以前 x * 3 / 2 + 1

    ​ JDK7.0及以后 x + (x >> 1)

    Vetor:分构造方法

    ​ Vetor(10) -> 2倍扩容 10 - 20 -30

    ​ Vetor(10,3) -> 定长扩容 10 -13 -16

  3. 出现版本不同可答可不答

    Vetor : 1.0

    ArrayList: 1.2

2.3 LinkedList

面试题

LinkedListArrayList之间的区别

  1. LinkedList和ArrayList的底层数据结构不同,导致优劣不同

    ArrayListLinkedList
    底层数据结构数组链表
    优点随机查找、遍历较快添加删除元素效率较高
    缺点添加删除元素效率低随机查找、遍历效率慢
2.4 Stack
  • 采用栈结构,先进后出
  1. 添加元素

    Stack<Integer> list = new Stack<>();
    list.push(666);
    
  2. 拉出元素

    System.out.println(list.pop());
    

3. Set

Set集合修改元素的步骤

public class RemoveTest{
    public static void main(String[] args){
    	Set<Integer> set = new HashSet<>();
        Collections.addAll(set,11,22,33,44,55);
        
        //1.创意一个临时的 同类型集合
        Set<Integer> temp = new HashSet<>();
        for(Iterator<Integer> car = set.iterator();car.hasNext();){
            if(car.next() == 55){
                //2.删除原有的元素
                car.remove();
                //3。吧修改后的元素放到临时集合中
                temp.add(45);
            }
        }
        //4.把修改之后的元素放回老集合中
        set.addAll(temp);
    }
}
3.1 HashSet
  • 没有顺序
  • 相同的元素只能添加一次
  • 所有涉及到下标的方法都没有了
  • 基于哈希表实现

HashSet的用法与ArrayList的用法基本一样但是所有跟下标有关的方法都不可以使用了

包含get()、remove()、for()遍历集合

  1. 如何创建对象

    HashSet<Integer> set = HashSet<>();
    
  2. 遍历集合

    for(Integer x : set){
        System.out.println(x);
    }
    for(Iterator<Iterger> car = set.iterator();car.hasNext();){
        System.out.println(set.next());
    }
    

HashSet的唯一性

唯一:内存里的同一个对象,不会添加多次

“唯一”:将两个不同的对象视为相等的对象取决于***hashCode()*** 和***equals()***

HashSet会根据传入对象的hashCode()得到的哈希码来决定具体分到哪一个组,如果两个对量的哈希码值是一样的话,才会调用equals来判断两个对象是不是一个对象,如果equals返回两个对象是一个对象的话,就不可以重复添加

当两个对象的哈希码值一样的时候,有3 种情况

①内存里的同一个对象、不可以重复添加

②视为相等对象、会调用equals()方法来是不是同一个对象

③重码

适用的方法有 add()、remove()、contains();

拓展

  1. 当有重复元素的时候,会抛弃新元素。老元素留着
  2. 当一个元素已经添加进HashSet集合的时候,不要随意修改参与生成hashCode()值的属性,如果一定要修改,要先删除后修改再添加
3.1.1 HashSet常用方法
  1. addAll

    import java.util.*;
    public class Exec1{
        public static void main(String[] args){
    		ArrayList<String> list = new ArrayList<>();
    		Collections.addAll(list,"张三","李四","李四","张三","王五");
    		HashSet<String> e = new HashSet<>();
            //将另外一个集合里边所有的东西撞到HashSet里边
    		e.addAll(list);
    		System.out.println(e);
    		System.out.println(e.size());
        }
    }
    
3.2 LinkedHashSet
  • 用法和HashSet一样
  • 作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
  • 优点:对于频繁的便利操作,优先考虑LinkedHashSet

3.3 TreeSet

  • 可以按照添加对象的指定属性进行排序

  • 向 TreeSet 中添加数据,要求是**相同类的对象**

  • 与 TreeMap 一样采用红黑树的存储结构

  • 有序,查询速度比List块

  • compareTo方法一样的返回0

    在使用TreeSet的时候应该尽量保证compareTo方法有可能返回0

    否则Tree Set集合的 add 方法用于不会认定有重复元素,无法保证唯一
    同时TreeSet的 remove 方法也不乏删除元素 ,
    add()、remove()、contains()、都是以来与compareTo()返回值是0的
    如果需求就是compareTo方法就是无法返回0的,必须借助迭代器的删除方法

    class User implements Comparable{
        private String name;
        private int age;
        //get  set 方法
        public User(String name,int age){
            this.name = name;
            this.age = age;
        }
        //重写hashCode 和 equals 方法
        @Override
        public int compareTo(Object o){
            if(o instanceof User){
                User u = (User)o;
                return this.getName().compareTo(u.getName());
            }else{
                throw new RuntimeException("输入的类型有误");
            }
        }
    }
    class TreeSetTest{
        @Test
        public void test(){
            TreeSet<User> t = new TreeSet<>();
            t.add("Tom",22);
            t.add("Lee",20);
            t.add("Wangwu",18);
        }
    }
    
3.3.1 TreeSet的遍历方式
//for Iterator
//lambda表达式
set.forEach(System.out::println);
set.forEach((x) -> System.out.println(x);)

3.3.2得到TreeSet的第一个和最后一个元素
//得到第一个元素
set.first();
//做后一个元素
set.last();
//pollFirst()选出并移除第一个元素

4. Map

Map
HashMap
LinkedHashMap
TreeMap
Hashtable
Properties
  • key 是不可以重复的,相当于用 Set 存储的,唯一无序
  • value 是可以重复的,无序
4.1 HashMap
  • HashMap 作为 Map 的 主要实现类,线程不安全,效率较高,可以存储 null 的 key 和 value
  • Hashtable 作为原始的实现类,线程安全,效率低,不能存储 null 的 key 和 value
public class MapTest{
    public static void main(String[] args){
    	Map map = new HashMap();
    }
}

默认容量是 16
默认加载因子是 0.75 也就是扩容的临界值是12

4.1.1常用方法
  1. put() 添加

    //添加元素
    map.put("AA",123);
    
  2. put() 修改

    //修改元素
    map.put("AA",1234);
    
  3. putAll()

    //将一个map添加到另外一个map中
    Map m = new HashMap();
    m.putAll(map);
    
  4. remove() 移除

    //remove(Object key)移除参数放key
    System.out.println(map.remove("CC"));
    
  5. clear() 清空数据

    map.clear();
    
  6. get() 获取指定 key 的value

    System.out.println(map.get("AA"));
    //当没有填入的参数的时候返回null
    
  7. containsKey / containsValue

    //判断当前map是否包含指定的key
    System.out.println(map.containsKey("AA"));//true
    //判断当前map是否包含指定的key
    System.out.println(map.containsValue(123));//true
    
  8. isEmpty() 判断当前map是否为空

    System.out.println(map.isEmpty());
    

    对元数据的操作

  9. keySet() 遍历map里所有的key

    Set set = map.keySet();
    for(Iterator i = set.iterator();i.hasNext()){
        System.out.println(i.next());
    }
    
  10. values() 遍历map里所有的value

    Collection c = map.values();
    for(Object o : c){
        System.out.println(c);
    }
    
  11. entrySet() 遍历所有的 key - value

    Set e = map.entrySet();
    for(Iterator i = e.iterator();i.hasNext();){
        //entrySet 集合中的元素都是 entry
        Map.Entry e = (Map.Entry)(i.next()); 
        System.out.println(e.getKey() + "--" + e.getValue());
    }
    
    1. getKey()

      //获取记录的键
      System.out.println(e.getKey());
      
    2. getValue()

      //获取对应的值
      System.out.println(e.getValue());
      
    3. setValue()

      //修改值
      e.setValue();
      

      无论我们使用keySet()、values()、entrySet()所得到的的都不是一个新的集合

    12.lambda表达式遍历Map集合

    map.forEach((k,v) -> System.out.println(k));
    map.forEach((k,v) -> System.out.println(v));
    map.forEach((k,v) -> System.out.println(k + ":" + v));
    

面试题:HashMap的底层实现原理

JDK 7以前

HashMap map = new HashMap();
  1. 在实例化以后,底层创建了一个长度为16的一维数组 Entry[] table
map.put(key1,value1);
  1. 调用 key1 所在类的 hashCode() 计算哈希值,在一定处理后(& 15),用来确定在Entry 数组中的存放位置。
  2. 如果此位置上没有数据,此时 key1 - value1 添加成功(添加的是value),
  3. 如果该位置有位置,意味着此位置有一个或多个数据,比较 key1 和已经存在的 key 的哈希值
    如果 key1 的哈希值与已经存在的数据的哈希值都不相同,就添加成功
    如果和以及存在的某一个相同,就判断equals,返回 false 就添加成功 返回 true 使用value 1 替换相同 key 的value值

JDK8底层实现有所不同

  1. new HashMap() : 底层没有创建一个长度为16的数组
  2. JDK 8 底层的数组是 Node[] 而非 Entry[]
  3. 首次调用 put() 方法,底层创建长度为 16 的数组
  4. JDK 7 底层结构只有数组 + 链表 JDK 8底层数据结构:数组 + 链表 + 红黑树
    当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前的数组长度 > 64 时,此时此索引位置上的所有数据改为使用红黑树存储
4.2 LinkedHashMap
  • 保证在遍历map元素时,可以按照添加的顺序实现遍历。
  • 在原有的 HashMap 的底层结构基础上,添加了一对指针,指向前一个和后一个元素。
  • 对于频繁的遍历操作,执行效率高于 HashMap
@Test
public void test(){
	Map map = new LinkedHashMap();
    map.put(1,"AA");
    map.put(1,"BB");
    map.put(1,"CC");
    System.out.println(map);
    
    Map map1 = new HashMap();
    map1.putAll(map);
    System.out.println(map1);
}
4.3 TreeMap
  • 保证按照添加的 key - value 对进行排序,实现排序遍历
  • 按照 key 进行排序,要求key必须是同一个类创建的对象
class Test{
    @Test
    public void test(){
        TreeMap map = new TreeMap();

        map.put(new User("Tom",23),98);
        map.put(new User("Jerry",21),91);
        map.put(new User("Jack",20),78);
        map.put(new User("Rose",22),58);
        //compareTo自然排序
        Set e = map.entrySet();
        for(Iterator car = e.iterator();car.hasNext();){
            Map.Entry et = (Map.Entry)(car.next());
            System.out.println(et.getKey() + "--" + et.getValue());
        }

    }
    @Test
    public void t2(){
        TreeMap map = new TreeMap(new Comparator(){
            @Override
            public int compare(Object o1,Object o2){
                if(o1 instanceof User && o2 instanceof User){
                 	User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compareTo(u1.getAge(),u2.getAge());
                }
            }
        });
    }
}
class User implements Comparable{
    private String name;
    private int age;
    
    public User(String name,int age){
        this.name = new name;
        this.age = new age;
    }
    //重写 toString(),equals(),compareTo()方法
}
4.4 Hashtable
4.4.1 Properties
  • 常用来处理配置文件, key 和 value 都是 String 类型
public class PropretiesTest{
    Propreties pros = new Propreties();
    FileInputStream fis = new FileInputStream("jdbc.properties");
    pros.load(fis);
    String name = pros.getProperty("name");
    String pwd = pros.getProperty("password");
}
//配置文件
/*
name=zhaojinhui
password=123456
*/

配置文件出现中文乱码解决方法

Setting – > File Encodings --> √ Transparent native-to-ascii conversion

5. Collections 工具类

  • 是一个操作**Collection 和 Map **的工具类
5.1 常用方法
方法作用
reverse(List)反转List中元素的顺序
shuffle(List)对List进行随机排序
sort(List)根据元素自然顺序队List里元素升序排列
sort(List,Comparator)根据指定的比较规则排序
swap(List,int i,int j)将List集合 i 处和 j 处元素交换
方法作用
max(Collection)根据自然顺序,返回最大值
max(Collection,Comparator)根据定制顺序返回最大值
min(Collection)根据自然顺序返回最小值
min(Collection,Comparator)根据定制顺序返回最小值
frequency(Collection,Object)返回指定元素出现的次数
copy(List i,List j)将 j 中的内容复制到 i 中
replaceAll(List i,Object o,Object n)用 n 替换 i 集合里边的 o
public class Test{
    public static void main(String[] args){
    	ArrayList<Integer> arr = new ArrayList<>();
        Collections.addAll(arr,1,2,3,4,5);
        List dest = Arrays.asList(new Object[arr.size()]);
        System.out.println(dest.size());
    }
}
  • Collections 类提供了多个synchronizedXxx() 方法,该方法可以将制定集合包装成线程同步的集合,从而解决多线程并发访问时产生的线程安全问题除了Vector (Stack) Hashtable,其他的集合都是线程不安全的
  • 在多线性高并发的情况下应该是用ConcurrentHashMap
List oldList = new ArrayList();
List newList = Collections.synchronizedList(oldLiset);

6. Java比较器

6.1 Comparable接口

自然排序 Comparable接口的使用

  • 像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个队想的方法

  • 重写compareTo(obj) 的规则:
    如果当前对象 this 大于形参对象 obj ,则返回正整数
    小于,返回负整数
    等于,返回0

  • 对于自定义类,如果需要排序,可以让自定义类实现Comparable接口,重写compareTo(obj)在方法中指明如何排序

    public class CompareTest{
        @Test
        public void test(){
            Goods[] arr = new Goods[5];
            arr[0] = new Goods("lenovo",34);
            arr[1] = new Goods("huawei",65);
            arr[2] = new Goods("dell",14);
            arr[3] = new Goods("xiaomi",43);
            arr[4] = new Goods("ausu",43);
            Arrays.sort(arr);
        }
        
    }
    class Goods implements Comparable{
        String name;
        double price;
        public Goods(String name,double price){
            this.name = name;
            this.price = price;
        }
        //指明按照什么方式排序
        @Override
        public int compareTo(Object o){
            if(o instanceof Goods){
                Goods g = (Goods)o;
                if(this.price > g.price){
                    return 1;
                }else if(this.price < g.price){
                    return -1;
                }else{
                    return this.name.copareTo(g.name);
                }
            }
            throw new RunTimeException("传入的数据类型不一致!");
        }
    }
    
6.2 Comparator接口

定制排序

  1. **当元素的类型没有实现Comparable接口而有不方便修改代码,或者实现了Comparable接口排序规则不适合当前的操作,name可以考虑使用Camparator排序,**强行队多个对象进行整体排序的比较
  2. 可以将Comparator传递到 sort 方法,从而允许在排序上实现精确控制(如Collections.sort 或Arrays.sort)
@Test
public void test(){
    Arrays.sort(arr,new Comparator<Goods>(){
       //按照产品名称从低到高
        @Override
        public int compare(Goods i,Goods j){
         if(i instanceof Goods && j instanceof Goods){
         	Goods g1 = (Goods)i;
            Goods g2 = (Goods)j;
             
            if(g1.getName().equals(g2.getName())){
            	return Double.compare(g1.getPrice(),g2.getPrice());    
            }else{
                return g1.getName().compareTo(g2.getName());
            }
         }   
        }
    });
}

测试类

@Data
public class Test implements Comparator<Test>{
    private Integer orders;
}

Lambda式简化版1

import iava.util.*;
public class TestComparator{
    public static void main(String[] args){
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list,22,33,77,14,44,55,66);
        Collections.sort(list,(x,y) -> x - y);
    }
}

Lambda简化版2

import iava.util.*;
public class TestComparator{
    public static void main(String[] args){
        List<Test> list = new ArrayList<>();
        COllections.addAll(list, ...);
        Comparator<Test> comp = Comparator.comparing(Test::getOrders);
        Collections.sort(list,comp);
}

Lambda不当人简化版简化版3

list.sort(Comparator.comparing(Test::getOrders));
6.3 两种接口的对比
  • Comparable 接口的方式一旦确定,保证Comparable接口实现类的对象在任何位置都可以比较大小
  • Comparator 属于临时性的比较
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值