java集合

集合

在编程时,可以使用数组来保存多个对象。使用数组的缺点是数组的长度不可变化,一旦初始化了数组的长度时,这个数组的长度就无法进行改变。如果想要保存数量在变化的数据时 ,使用数组的方法就不可取了。

为了保存数量不确定数据,以及保存具有映射关系的数据,Java提供了Collection和Map接口。

一、Collection集合

Collection接口中声明的方法:
  public boolean add(E e):把给定的对象添加到当前集合中。
  public void clear() :清空集合中所有的元素。
  public boolean remove(E e): 把给定的对象在当前集合中删除。
  public boolean contains(E e): 判断当前集合中是否包含给定的对象。
  public boolean isEmpty(): 判断当前集合是否为空。
  public int size(): 返回集合中元素的个数。
  public Object[] toArray(): 把集合中的元素,存储到数组中。

1、Collection的子接口——List接口

是最常用的接口。是有序集合,允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置,用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,与数组类似。

List接口的实现类:
集合名底层实现原理特点
ArrayList数组查询快,增删慢
LinkedList链表查询慢,增删快
Vector(了解)数组和ArrayList相似,但是它是线程安全的
List接口中特有的方法:
public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
② public E get(int index):返回集合中指定位置的元素。
③ public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
④ public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值为更新前的元素。
(1)ArrayList
(1) 什么是ArrayList

java.util.ArrayList 是大小可变的数组的实现,存储在内的数据称为元素。此类提供一些方法来操作内部存储的元素。 ArrayList 中可不断添加元素,其大小也自动增。

(2)ArrayList的声明

格式: ArrayList 名称=new ArrayList<>();

E:表示泛型:表示该集合要存储哪种引用类型的数据。如果要存储字符串就写为

举例:

//1.声明一个存储整数类型的ArrayList
ArrayList<Integer> intList=new ArrayList<>();
 
//2.声明一个存储字符串类型的ArrayList
ArrayList<String> strList=new ArrayList<>();

//3.声明一个存储学生对象的ArrayList
ArrayList<Student> stuList=new ArrayList<>();

//4.使用List多态形式创建:List是ArrayList的父接口
List<Integer> list=new ArrayList<>();

//5.使用Collection多态的形式创建:Collection是ArrayList的爷接口
//缺点:无法使用子接口或者子类中的特有方法。
Collection<Integer> list2=new ArrayList<>();

**注意事项:**ArrayList对象不能存储基本类型,只能存储引用类型的数据。所以不能写ArrayList等,但是存储基本数据类型对应的包装类型是可以的。

(3)ArrayList类的基本方法

1.public boolean add(E e):往集合中添加元素,默认添加到尾部。

2.public int size():返回集合中的元素个数

3.public E remove(int index或者Object obj):删除指定下标的元素或者元素,返回删除的值

4.public E get(int index):返回指定下标的值

举例:

//1.public boolean add(E e):往集合中添加元素,默认添加到尾部。
	   ArrayList<Integer> intList=new ArrayList<>();
	   intList.add(1);
	   intList.add(-1);
	   intList.add(3);
	   System.out.println(intList);//[1, -1, 3]

//2.public int size():返回集合中的元素个数
       System.out.println(intList.size());//3
       
//3.public E remove(int index或者Object obj):删除指定下标的元素或者元素,返回删除的值
       int i=intList.remove(0);//1
       
//4.public E get(int index):返回指定下标的值
       int j= intList.get(0);
(2) LinkedList

LinkedList集合数据存储的结构是链表结构。方便元素添加、删除。是一个双向链表。

LinkedList集合提供了大量首尾操作的方法(特有):

public void addFirst(E e) :将指定元素插入此列表的开头。
② public void addLast(E e) :将指定元素添加到此列表的结尾。
③ public E getFirst() :返回此列表的第一个元素。
④ public E getLast() :返回此列表的最后一个元素。
⑤ public E removeFirst() :移除并返回此列表的第一个元素。
⑥ public E removeLast() :移除并返回此列表的最后一个元素。
⑦ public E pop() :从此列表所表示的堆栈处弹出一个元素。
⑧ public void push(E e) :将元素推入此列表所表示的堆栈。
⑨ public boolean isEmpty() :如果列表不包含元素,则返回true
List集合的遍历

声明一个存储字符串类型的ArrayList,并添加元素

       ArrayList<String> strList=new ArrayList<>();
	   strList.add("富强");
	   strList.add("民主");
	   strList.add("文明");
	   strList.add("和谐");
	   strList.add("自由");

1. for循环遍历:

//通过size()方法获取到集合的长度 
for(int i=0;i<strList.size();i++) {
    //使用get(int index)方法根据下标获取元素
	 System.out.println(strList.get(i));	   
 }

2. 增强for循环遍历:

   for(String str:strList) {
	 System.out.println(str);
 }

3. 使用迭代器进行遍历:

使用步骤:

1.使用public Iterator iterator()来获取集合对应的迭代器

2.通过it引用使用public boolean hasNext()来判断是否还有元素进行迭代,如果有则返回true

3.使用next()方法获取元素

迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

//1.使用public Iterator iterator()来获取集合对应的迭代器
   Iterator<String> it= strList.iterator();

//2.通过it引用使用public boolean hasNext()来判断是否还有元素进行迭代,如果有则返回true
	while(it.hasNext()) {
        //3.使用next()方法获取元素。
		   System.out.println(it.next());
	   }
	   

4.使用jdk1.8新特性foreach进行遍历

格式:

list名称.forEach(变量名->{

System.out.println(变量名);

})

//item表示每次获取到的元素   
     strList.forEach(item->{
    	System.out.println(item);    
     });

练习:用上述方式遍历linkedList

总结:

1.当需要对数据进行大量的查询时,使用ArrayList,底层是数组实现,查询快,增删慢。

2.当需要对数据进行大量的增删操作时,使用LinkedList,底层是链表实现,增删快,查询慢。

2、Collection的子接口——Set接口

同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加 严格了。与 List 接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

名称底层实现特点
HashSet哈希表 + 链表+红黑树无索引,不可存储重复元素,存取无序
LinkedHashSet哈希表 + 链表无索引,不可存储重复元素,保证存取顺序
TreeSet(了解)二叉树一般就用于排序
1、HashSet

特点:

1.不允许存储重复的元素。

2.没有索引,没有带索引操作的方法,所以不能使用普通的for循环进行遍历。

3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致。

4.底层是一个哈希表结构。(查询速度非常快)。

注:HashSet是根据对象的哈希值来缺点元素在集合中的存储位置,因此具有良好的存储和查找性能。保证元素唯一性的方式依赖于:hashcode和equals方法。

(1)HashSet的声明
	//1.声明存储整型的HashSet
	HashSet<Integer> intSet=new HashSet<>();
    intSet.add(12);
	intSet.add(11);
	intSet.add(10);
	System.out.println(intSet);//[10,11,12]

   //2.使用多态的方式进行声明
    Set<String> strSet=new HashSet<>();
	strSet.add("徐悲鸿画马");
	strSet.add("齐白石画虾");
	strSet.add("周杰伦画饼");
(2)HashSet元素的唯一性
	//声明一个能存储字符串类型的hashSet
    HashSet<String> objSet=new HashSet<>();
    //添加重复的元素
	objSet.add("kaler");
	objSet.add("kaler");
    objSet.add("young");
    objSet.add("young");
	System.out.println(objSet);//[kaler, young] 打印出来的元素是不重复的

HashSet是如何实现元素不重复的?

存储的数值100125150200225
hashcode值(举例)1232132112456546544578998
通过Hash算法计算存储位置02122

存储逻辑:

首先计算出存储元素元素的hash(比如123),去集合中查询是否已经存储了hash值为123的这个元素。如果没有则直接添加,如果已经存在,使用equals方法去比较存储的内容是否相同,相同则不添加,不相同则在相同hash值存储元素的下方使用链表进行存储。

hashCode

举例:

    //一般来说,equals相同,hashcode相同
     String str="abc";
	 String str2=new String("abc");
	 System.out.println(str.equals(str2));//true
	 System.out.println(str.hashCode());//96354
	 System.out.println(str2.hashCode());//96354  

  //equals不同,hashcode不一定不同
     System.out.println("通话".hashCode());//1179395
	 System.out.println("重地".hashCode());//1179395
(2)HashSet存储自定义类型的元素

定义学生类:

public class Student {
    //成员变量
	private String name;
	private int age;
   
	//有参构造方法
	public Student(String name,int age) {
		this.name=name;
		this.age=age;
	} 
	
    //重写toString方法
 @Override
public String toString() {
	return "[姓名:"+this.name+",年龄"+this.age+"]";
  }

}

     HashSet<Student> stuSet=new HashSet<>();
     stuSet.add(new Student("彭于晏", 18));
     stuSet.add(new Student("陈冠希", 19));//100
     stuSet.add(new Student("陈冠希", 19));//123
     stuSet.add(new Student("吴彦祖", 20));
     System.out.println(stuSet);
/*
打印结果:
[姓名:陈冠希,年龄19], 
[姓名:陈冠希,年龄19], 
[姓名:吴彦祖,年龄20],
[姓名:彭于晏,年龄18]
*/

注:发现上述存储的对象不唯一。由于每个对象的地址值不同,所以hashcode值不同。因此是直接可以存储的。

如何保证对象元素的唯一?

解决:重写hashcode方法

思路:因为在存储时只是计算new Student()对象的hash值,每个对象地址不一致,因此hashCode肯定也不一致,HashSet是直接存储的。是跟成员变量的值无关的。但是我们可以通过重写使用name和age两个成员变量来计算其hashcode,此时hashcode的值就和成员变量有关。

总结:对象的name和age一样时,让对象的hashcode一样,不让HashSet直接存储。

(1) 在Student类下重写hashcode方法

@Override
public int hashCode() {
    //返回name值的hashcode+年龄
	return this.name.hashCode()+this.age;
}

此时name和age相同的对象,hashCode一致了,此时两个对象的equals比较还是false,因此还是会存储进去。

(2) 再在Student类下重写equals方法

//逻辑:判断成员变量name和age是否都一致,如果一致返回true。
@Override
public boolean equals(Object obj) {
 if(obj instanceof Student) {
	Student stu=(Student)obj;
	if(stu.name.equals(this.name) && stu.age==this.age) {
		return true;
	} 
 }
 return false;
}

总结:如果要保证存储对象的唯一性。根据HashSet存储元素时的判断,就必须同时重写hashCode和equals方法。保证equlas不同时,hashCode不同。

//打印结果:此时就没有重复的对象了。
[姓名:彭于晏,年龄18],[姓名:吴彦祖,年龄20],
[姓名:陈冠希,年龄19]
2、LinkedHashSet

操作和HashSet相似,底层是由哈希表和链表实现。

3、TreeSet

TreeSet会对存储的元素进行自动排序。

     TreeSet<Integer> tree=new TreeSet<>();
     tree.add(10);
     tree.add(7);
     tree.add(18);
     tree.add(4);
     System.out.println(tree);//[4, 7, 10, 18]

     TreeSet<String> strTree=new TreeSet<>();
     strTree.add("cba");
     strTree.add("aba");
     strTree.add("bba");
     System.out.println(strTree);//[aba, bba, cba] 根据首字母进行排序
Set的遍历

由于Set集合没有索引,没有带索引操作的方法,所以不能使用普通的for循环进行遍历。

1.使用增强for循环遍历。

2.使用iterator迭代器进行遍历。

3.使用jdk1.8新特性进行遍历。

4、Collections工具类

常用方法

1.public static boolean addAll(Collection,T…element),往集合里面添加一些元素

2.public static void shuffle(List<?> list):打乱集合顺序

3.public static void sort(List list):将集合中元素按照默认规则排序

4.public static void sort(List list,Comparator<? super T> ) :将集合中元素按照

//1.public static <T> boolean addAll(Collection<T>,T....element),往集合里面添加一些元素
List<Integer> list=new ArrayList<>();
Collections.addAll(list, 89,100,20,46);
System.out.println(list);//[89, 100, 20, 46]
		
//2.public static void shuffle(List<?> list):打乱集合顺序
Collections.shuffle(list);
System.out.println(list);//[20, 46, 100, 89]
	    
//3.public static <T> void sort(List<T> list):将集合中元素按照默认规则排序
Collections.sort(list);
System.out.println(list);//[20, 46, 89, 100]

 //4.public static <T> void sort(List<T> list,Comparator<? super T> ) :将集合中元素按照
5、比较器–Comparator接口

根据上述代码,使用sort方法是对元素默认进行升序排列,如何按照降序进行排序。这就需要使用Comprator比较器自定义规则。

使用方法:public static void sort(List list,Comparator<? super T> )

 List<Integer> list=new ArrayList<>();
 Collections.addAll(list, 89,100,20,46);
 System.out.println(list);//[89, 100, 20, 46]

//使用匿名内部类的方式,创建Comparator接口对象,并重写compara方法
 Comparator<Integer> com= new Comparator<>() {
	  public int compare(Integer num1, Integer num2) {
			 return num2-num1;//表示相邻两个数进行比较,>0交换位置
	   }
	 };
//使用sort方法,并传入list,和Comparator对象
 Collections.sort(list, com);
 ystem.out.println(list);//[100, 89, 46, 20]

如果对自定义类型进行排序。比如,按照自定义学生类的名字或者年龄进行排序

示例:

定义一个学生类

public class Student {
	String name;
	int age;
	public Student() {
		
	}
	
	public Student(String name,int age) {
		this.name=name;
		this.age=age;

	}
	
	@Override
	public String toString() {
	 
		return "姓名:"+this.name+",年龄:"+this.age;
	}
	
}

测试:

public static void main(String[] args) {
		ArrayList<Student> list=new ArrayList<>();
		list.add(new Student("zhangsan",18));
		list.add(new Student("lisi",20));
		list.add(new Student("wangwu1",16));
		list.add(new Student("zhaoliu",30));
    
    //使用匿名内部类创建Comparator接口的对象,并重写compara方法。
		Comparator<Student> com=new Comparator<Student>() {
			@Override
			public int compare(Student s1, Student s2) {
				//按照name的长度升序排序
                int i=s1.name.length()-s2.name.length();
                if(i==0) {//如果名字的长度一致
                	i=s1.age-s2.age;//按照年龄的大小升序排序
                }
				return i; 
			}
			
		};
    //调用sort方法传递Comparator对象进行排序
		Collections.sort(list,com);
    //遍历集合
	     for(Student stu:list) {
	    	 System.out.println(stu);
	     }
		
	}
//打印结果:
姓名:lisi,年龄:20
姓名:wangwu1,年龄:16
姓名:zhaoliu,年龄:30
姓名:zhangsan,年龄:18
6.比较器–comparable接口

在Student类上实现Comparable接口,并重写CompareTo方法

//实现Comparable接口
public class Student implements Comparable<Student>{
	String name;
	int age;
	public Student() {
		
	}
	
	public Student(String name,int age) {
		this.name=name;
		this.age=age;

	}
	
	@Override
	public String toString() {
	 
		return "姓名:"+this.name+",年龄:"+this.age;
	}

    //重写ComparaTo方法
	@Override
	public int compareTo(Student o) {
        //按照name的长度升序排序
	    int i=this.name.length()-o.name.length();
	    if(i==0) {//如果名字的长度一致
	    	i=this.age-o.age;//按照年龄的大小升序排序
	    }
		return i;
	}

}

测试:

	public static void main(String[] args) {
		ArrayList<Student> list=new ArrayList<>();
		list.add(new Student("zhangsan",18));
		list.add(new Student("lisi",20));
		list.add(new Student("wangwu1",16));
		list.add(new Student("zhaoliu",30));
        //使用sort方法进行排序
		Collections.sort(list);
	     for(Student stu:list) {
	    	 System.out.println(stu);
	     }
	}
//打印结果:
姓名:lisi,年龄:20
姓名:wangwu1,年龄:16
姓名:zhaoliu,年龄:30
姓名:zhangsan,年龄:18

二、Map

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象。

  1. 在Collection接口中,元素都是单个存在的。

  2. Map 中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。

  3. Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

Map中常用的实现类:

实现类底层实现特点
HashMap<K,V>哈希表+链表+红黑树元素的存取顺序可能不一致,允许null值null键
LinkedHashMap<K,V>哈希表+链表+红黑树为HashMap的子类,存取顺序一致,允许null值null键
TreeMap<K,V>红黑树根据key值排序,不允许null值null键

注:Map中的键-值实质上是存储在Entry对象中的。

1、HashMap

(1)HashMap<K,V>的声明;(K:表示键的类型。V:表示值的类型)

 //声明一个键值都是字符串类型的HashMap
 HashMap<String,String> ssmap=new HashMap<>();
 //使用多态方式声明
 Map<Integer,String> ismap=new HashMap<>();

(2)HashMap的常用操作

1 public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。

2.public V get(Object key) :根据指定的键,在Map集合中获取对应的值。

3.boolean containsKey(Object key) / containsValue(Object value) :判断集合中是否包含指定的键或指定的值。

4.public V remove(Object key) : 删除指定的键所对应的键值对元素,返回被删除元素的值。

5.public Set keySet() : 获取Map集合中所有的键,存储到Set集合中。

6.public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。

 //声明一个HashMap
 HashMap<String,String> cpMap =new HashMap<>();

 //1 public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
 cpMap.put("霞", "洛");
 cpMap.put("EZ", "拉克丝");
 cpMap.put("泰达米尔", "艾希");
 System.out.println(cpMap);//{EZ=拉克丝, 霞=洛, 泰达米尔=艾希}

//2.public V get(Object key) :根据指定的键,在Map集合中获取对应的值。
 cpMap.get("EZ")//拉克丝

//3. boolean containsKey(Object key) / containsValue(Object value) :判断集合中是否包含指定的键或指定的值。     
 cpMap.containsKey("霞");//ture
 cpMap.containsValue("洛");//ture

//4.public V remove(Object key) : 删除指定的键所对应的键值对元素,返回被删除元素的值。
 cpMap.remove("霞");//返回"洛"

//5.public Set<K> keySet() : 获取Map集合中所有的键,存储到Set集合中。
 Set<String> set=cpMap.keySet();//[EZ, 霞, 泰达米尔]

//6.public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
 Set<Map.Entry<String, String>> sets= cpMap.entrySet();//[EZ=拉克丝, 霞=洛, 泰达米尔=艾希]

(3)HashMap插入重复的key

代码示例:

 HashMap<String,Integer> map =new HashMap<>();
 map.put("盖伦", 18);
 map.put("赵信", 19);
 map.put("嘉文四世", 20);
 System.out.println(map);//{嘉文四世=20, 赵信=19, 盖伦=18}
 
//添加一个重复的key
 map.put("盖伦", 30);
 System.out.println(map);//{嘉文四世=20, 赵信=19, 盖伦=30} 此时盖伦所对应的值被后来添加的value覆盖了

注:在hashMap中插入重复的key时,value会被后来插入的value覆盖掉。

**(4) ** HashMap的遍历

方式一:通过key去查找,使用keySet()方法获取到一个键集合,遍历该key,再使用get(K key)方法获取值。

 HashMap<String,Integer> map =new HashMap<>();
 map.put("盖伦", 18);
 map.put("赵信", 19);
 map.put("嘉文四世", 20);
 map.put("艾希", 21);
 map.put("李青", 22);

//使用keySet()方法获取到一个键Set集合
 Set<String> keys=map.keySet();
 for(String key:keys) {
    System.out.println(key+":"+map.get(key));
        	
 }

方式二:HashMap中-=的每个key-value是存储在Entry这个对象中。因此我们要遍历出元素,首先要先获取到每个Entry对象。使用entryset()方法就可以获取到每个Entry,再使用Entry中的getKey()和getValue()方法来获取到键和值。

 //获取到Entry对象的Set集合·
 Set<Map.Entry<String, Integer>> entries=map.entrySet();

 //遍历得到每个Entry对象
  for(Map.Entry<String, Integer> entry:entries) {
     //使用getKey()方法获取键  getValue()方法获取值
 System.out.println(entry.getKey()+":"+entry.getValue());
  }

(4)HashMap存储自定义类型的值

定义一个People类

public class People {
	String name;
	int age;
	
	public People() {
	
	}
	public People(String name,int age) {
		this.name=name;
		this.age=age;
	}
	
	@Override
	public String toString() {
	 
		return "名称:"+this.name+",年龄:"+this.age;
	}

}

测试:

 HashMap<People,String> userMap=new HashMap<>();
 userMap.put(new People("zhangsan",18),"成都");
 userMap.put(new People("lisi",20),"北京");
 userMap.put(new People("wangwu",19),"上海");
 userMap.put(new People("zhaoliu",18),"重庆");
		
 //遍历
 Set<People>sets=userMap.keySet();
 for(People people:sets) {
 System.out.println(people+" 地址:"+userMap.get(people));
 }

补充:

1. HashTable

HashTable是线程安全,HashMap线程不安全。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值