天天看点

集合即容器!集合即容器!

集合即容器!

开发和学习中需要时刻和数据打交道,如何组织这些数据是我们编程中重要的内容。我们一般通过“容器”来容纳和管理数据。事实上,我们前面所学的数组就是一种容器,可以在其中放置对象或基本类型数据。

1. 数组

数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。

数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那这样的用户有多少个?在写程序时是无法确定的。因此,在这里就不能使用组。

==> 基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器。容器(Collection)也称之为集合。

集合:

存放对象的容器

1. 只能用于存放对象

Java中每一种基本数据类型都有对应引用类型

2. 集合存放的是多个对象的引用,对象本身还是存放在堆中

3. 集合可以存放不同类型,不限数量的数据类型

2. 容器的结构

2.1 结构图

单例集合: 将数据一个一个的进行存储。

双例集合:基于Key与Value的结构存储数据。

集合即容器!集合即容器!

3. Collection接口

Collection是单例集合类的顶级接口。它提供对集合对象进行基本操作的通用接口方法。它的直接继承接口有List,Set和Queue.

Collection 接口中的抽象方法

方法 说明
boolean add(Object element) 增加元素到容器中
boolean remove(Object element) 从容器中移除元素
boolean contains(Object element) 容器中是否包含该元素
int size 容器中元素的数量
boolean isEmpty() 容器是否为空
void clear 清空容器中所有元素
Iterator iterator() 获得迭代器,用于遍历所有元素
boolean containsAll(Collection c) 本容器是否包含c容器中的所有元素
boolean addAll(Collection c) 将容器c中所有元素增加到本容器
boolean removeAll(Collection c) 移除本容器和容器c中都包含的元素
boolean retainAll(Collection c) 取本容器和容器中都包含的元素,移除非交集元素
Object[] toArray() 转化成Object 数组

由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。

4. List 接口介绍

4.1 List 的特点

  • 有序:有序(元素存入集合的顺序和取出的顺序一致)。List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。
  • 可重复:List允许加入重复的元素。更确切地讲,List通常允许满足e1.equals(e2) 的元素重复加入容器。

4.1 List 的常用方法

除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法,参见下表:

方法 说明
void add (int index, Object element) 在指定位置插入元素,以前元素全部后移一位
Object set (int index,Object element) 修改指定位置的元素
Object get (int index) 返回指定位置的元素
Object remove (int index) 删除指定位置的元素,后面元素全部前移一位
int indexOf (Object o) 返回第一个匹配元素的索引,如果没有该元素,返回-1.
int lastindexOf (Object o) 返回最后一个匹配元素的索引,如果没有该元素,返回-1

4.3 ArrayList 容器类

ArrayList 是 List 接口的实现类。是 List 存储特征的具体实现。所以能够实现Collection和List接口的方法。 ArrayList底层是用数组实现的存储。

特点:查询效率高,增删效率低,线程不安全!

Vector 线程是安全的(不推荐使用)

package day04;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
	public static void main(String[] args) {
	//直接new一个ArrayList类
		List<String> list = new ArrayList<String>();
        //往容器添加元素
		list.add("5");
		list.add("2");
		list.add("0");
		System.out.println(list.get(2));//允许使用重复元素,可通过索引来访问指定位置的元素
		
		//默认按元素添加位置设置元素索引
        //按索引在指定位置添加:list.add(0, "6");
		list.add(0, "6");
		System.out.println(list);
		
		List<String> l = new ArrayList<String>();
		l.add("123");
		l.add("456");
        //插入集合
		list.addAll(l);
        //指定位置插入集合:list.addAll(2, l);
		list.addAll(2, l);
		System.out.println(list);
		System.out.println(list.indexOf("5"));//第一个下标:list.indexOf("5")
		System.out.println(list.lastIndexOf("5"));//最后一个下标:list.lastIndexOf("5")
		list.set(1, "你好");//修改指定元素:list.set(1, "你好");
		System.out.println(list);
		//截取指定位置元素:List<String> sub = list.subList(1, 2);
		List<String> sub = list.subList(1, 2);
		System.out.println(sub);
		System.out.println(list.size());//指定数组的长度
	}
}
           

4.5 Vector 容器类

  • Vector底层是用数组实现的,相关的方法都加了同步检查,因此“线程安全效率低”比如,indexOf方法就增加了synchronized 同步标记。
  • Vector的使用与Arraylist是相同的,因为他们都实现了List 接口,对 List 接口中的抽象方法做了具体实现。

    4.5.1.Stack 容器

  • Stack栈容器是Vector的一个子类它实现了一个标准的后进先出(LIFO:Last In Frist Out)的栈。
  • Stack 特点是后进先出。它通过5个操作方法对 Vector进行扩展,允许将向量视为堆栈。

4.6 LinkedList 容器类

LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

集合即容器!集合即容器!
class Node<E>{
    E item;
    Node<E> next;
    Node<E> prev;
}//定义双向链表
           

LinkedList的使用(非 List标准)

专门用来操作双向链表

方法 说明
void addFirst(E e) 将指定元素插入到开头
void addLast(E e) 将指定元素插入到结尾
getFirst() 返回此列表的第一个元素
getLas() 返回此列表的最后一个元素
removeFirst() 移除此列表中的第一个元素,并返回这个元素
removeLast() 移除此列表中的最后一个元素,并返回这个元素
E pop() 从此列表所表示的堆栈处弹出一个元素,等效于 removeFirst
void push(E e) 将元素推入此列表所表示的堆栈这个等效于addFisrt(E e)
boolean isEmpty() 判断列表是否包含元素,如果不包含元素则返回true

5. Set 接口介绍

Set接口继承自Collection ,Set 接口中没有新增方法,方法和 Collection 保持完全一致。

5.1 Set 接口特点

Set特点:无序不可重复。无序指Set中的元素没有索引,我们只能遍历查找;不可

重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()

方法对比为true,则只能保留一个。

Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet。

5.2 HashSet存储特征分析

HashSet是一个不保证元素的顺序且没有重复元素的集合,是线程不安全的。HashSet允许有null元素。

无序:

在HashSet中底层是使用HashMap存储元素的。HashMap 底层使用的是数组与链表实现元素的存储。元素在数组中存放时,并不是有序存放的也不是随机存放的,而是对元素的哈希值进行运算决定元素在数组中的位置

不重复:

当两个元素的哈希值进行运算后得到相同的在数组中的位置时,会调用元素的equals()方法判断两个元素是否相同。如果元素相同则不会添加该元素,如果不相同则会使用单向链表保存该元素。

总结就是以下几点:

  • 不能保证元素的排列顺序
  • 调用hashcode()获得hashcode值来确定在集合的位置
  • 不可重复

    指的是hashcode()返回的值不相同

  • 不是线程安全的
  • 集合元素可以使用null
  • 判断元素相等的标准

    equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

    如果两个对象通过equals()方法返回true,这两个对象的 hashCode 值也应该相同。

5.3 TreeSet 容器类

TreeSet是一个可以对元素进行排序的容器。底层实际是用 TreeMap 实现的,内部维

持了一个简化版的TreeMap通过key来存储Set的元素。TreeSet内部需要对存储的元

素进行排序,因此,我们需要给定排序规则。

排序规则实现方式:

==>通过元素自身实现比较规则。

==>通过比较器指定比较规则。

//自然排序 排数字:Set<Integer> set = new TreeSet<Integer>();
// 比较器指定:对人按年龄排序:Set<Person> e = new TreeSet<Person>(new Person());
package day04;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import sun.reflect.generics.tree.Tree;
public class Test2 {
	public static void main(String[] args) {
		
//		Set<Integer> set = new TreeSet<Integer>();
//		set.add(1);
//		set.add(6);
//		set.add(2);
//		set.add(3);
//
//		//迭代输出
//		Iterator<Integer> it = set.iterator();
//	  while(it.hasNext()) {
//			System.out.println(it.next());	
//			}
		Person p1 = new Person("张三",18);
		Person p2 = new Person("李四",15);
		Person p3 = new Person("王五",38);
		Person p4 = new Person("梁六",26);
		Person p5 = new Person("何七",21);
		
		Set<Person> e = new TreeSet<Person>(new Person());
		e.add(p1);
		e.add(p2);
		e.add(p3);
		e.add(p4);
		e.add(p5);
        //迭代输出
	for(Person p :e) {
		System.out.println("姓名:" + p.name + " " + "年龄:" + p.age );
	}		
   }
}
//通过比较器比较要实现Comparator接口
class Person implements Comparator<Person>{
	int age;
	String name;
	
	public Person() {
		
	}
    
public Person(String name,int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public int compare(Person args0, Person args1) {
		if(args0.age < args1.age) {
			return 1;
		}else if(args0.age > args1.age) {
			return -1;
		}else {
		return 0;
		}
	}
}
           

6. Map 接口介绍

Map接口定义了双例集合的存储特征,它并不是 Collection 接口的子接口。双例集合的存储特征是以key与value结构为单位进行存储。体现的是数学中的函数y=f(x)感念。

Map 与 Collecton 的区别:

  • Collection中的容器,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
  • Map中的容器,元素是成对存在的(理解为现代社会的夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
  • Collection中的容器称为单列集合,Map中的容器称为双列集合。
  • Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
  • Map 中常用的容器为 HashMap,TreeMap等。

6.1 Map的常用方法

方法 说明
V put (K key, V value) 把key 与 value 添加到Map集合中
void putll(Map m) 从指定Map中将所有映射关系复制到此Map中
V remove (Object key) 删除key 对应的 value
V get(Object key) 根据指定的key,获取对应的value
boolean containsKey(Object key) 判断容器中是否包含指定的key
boolean containsValue(Object value) 判断容器中是否包含指定的value
Set keySet() 获取Map集合中所有的key,存储到Set集合中
Set<Map.Entry<K,V>>entrySet() 返回一个Set基于Map.Entry类型包含Map中所 有映射。
void clear() 删除Map中所有的映射

6.2 HashMap容器类

HashMap是Map接口的接口实现类,它采用哈希算法实现,是 Map 接口最常用的实现类。由于底层采用了哈希表存储数据,所以要求键不能重复,如果发生重复,新的值会替换旧的值。 HashMap在查找删除修改方面都有非常高的效率。
package day04;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
//import java.util.TreeMap;
/**
 *注解部分是另外一个举例
 */
public class Test4 {
	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 5);//添加元素
		map.put("b", 2);
		map.put("c", 0);
		System.out.println(map);
		System.out.println(map.get("b"));//根据Key取值
		map.remove("c");//移除
		System.out.println(map);
		System.out.println(map.size());//长度
		System.out.println(map.containsKey("b"));//判断集合是否包含指定的Key
		System.out.println(map.containsValue(5));//判断集合是否包含指定的Value
        
		//①通过map.keySet()遍历集合
		Set<String> keys = map.keySet();//获取集合所有的Key值
         map.values();//获取集合所有Value值
		//遍历集合
		for(String key : keys ) {
			System.out.println("Key:" + key + ",values:" + map.get(key));
		}	
		//②通过map.entrySet()遍历
		Set<Entry<String, Integer>> entrys = map.entrySet();
		//遍历
		for(Entry<String, Integer> en : entrys) {
			System.out.println("Key:" + en.getKey() + ",values:" + en.getValue());
		}		
	}
}
           

6.3 TreeMap 容器类

TreeMap 和 HashMap 同样实现了 Map 接口,所以,对于 API 的用法来说是没有区别的。HashMap效率高于TreeMap;TreeMap是可以对键进行排序的一种容器,在需要对键排序时可选用TreeMap。TreeMap底层是基于红黑树实现的。

在使用TreeMap时需要给定排序规则:

元素自身实现比较规则。

通过比较器实现比较规则。

public class Test5 {
	public static void main(String[] args) {
		Map<Integer,String> map = new TreeMap<Integer, String>();//数字排序
		map.put(2, "2");
		map.put(1, "5");
		map.put(6, "0");
		System.out.println(map);
		Map<String,String> map1 = new TreeMap<String, String>();//字符串自然排序:字典排序
    }
}
           

7. Iterator 迭代器

7.1 Iterator 迭代器接口介绍

Collection接口继承了Iterable接口,在该接口中包含一个名为iterator的抽象方法,所有实现了Collection接口的容器类对该方法做了具体实现。iterator方法会返回一个Iterator接口类型的迭代器对象,在该对象中包含了三个方法用于实现对单例容器的迭代处理

7.2 Iterator接口方法

booleanhasNext();//判断游标当前位置是否有元素,如果有返回true,否则返回false;
 Object next(); //获取当前游标所在位置的元素,并将游标移动到下一个位置
 void remove(); //删除游标当前位置的元素,在执行完next后该操作只能执行一次;
           

7.3 集合的遍历

//迭代器的举例
		Iterator<Object> it = set.iterator();
//用while迭代
		while(it.hasNext()) 
			{System.out.println(it.next());}
//用for each迭代	 
		for(Object obj : set)
			{//把set每一个值取出来,赋值给obj,直到循环set的所有值
			System.out.println(obj);
			}
System.out.println(set.size());//输出数组长度:
           

8 操作集合的工具Collections工具类

Collections是一个工具类,它提供了对SetListMap进行排序、填充、查找元素的辅助方法。该类中所有的方法都为静态方法。

8.1 常用方法

常用方法 作用
void sort(List) 对List容器内的元素排序,排序的规则是按照升序进行排序
void shuffle(List) 对List容器内的元素进行随机排列。
void reverse(List) 对 List 容器内的元素进行逆续排列
void fill(List Object) 用一个特定的对象重写整个List 容器。
int binarySearch(List Object) 对于顺序的List容器,采用折半查找的方法查找特定对象

8.2 举例

package day04;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test6 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("i love you");
		list.add("Me too!");
		list.add("really?");
		list.add("yes!");
		list.add("yes!");
		System.out.println(list);
//用来操作集合		
		Collections.reverse(list);//反转元素
		System.out.println(list);
		Collections.shuffle(list);//随机排序
		System.out.println(list);
		Collections.sort(list);//自然顺序按升序排列
		System.out.println(list);
        //查找指定集合中元素出现的次数
		System.out.println(Collections.frequency(list, "yes!"));
        //替换
		Collections.replaceAll(list, "yes!", "of course!");
		System.out.println(list);
		Collections.swap(list, 1,2 );//指定元素交换
		System.out.println(list);
		
	 System.out.println(Collections.max(list));//元素自然顺序找最大最小值
	 System.out.println(Collections.min(list));
        
/**
 *下面的是另一个举例
 */
		Students s1 = new Students(14,"张三");
		Students s2 = new Students(18,"李四");
		Students s3 = new Students(12,"王五");
		
		List<Students> stus = new ArrayList<Students>();
		stus.add(s1);
		stus.add(s2);
		stus.add(s3);
        
        //按Comparator中指定顺序返回元素	
        //第一个参数是要排序的集合。第二个是对象
		Students s = Collections.max(stus, new Students());
		System.out.println(s.name + s.age);
		for(Students stu : stus) {
			System.out.println("姓名" + stu.name + " " + "年龄" + stu.age);
		}
		System.out.println("-------------------");
		Collections.sort(stus, new Students());
       for(Students stu : stus) {
			System.out.println("姓名" + stu.name + " " + "年龄" + stu.age);
		}
	}
}

//创键一个student类
class Students implements Comparator<Students>{
	int age;
	String name;
	public  Students () {
	}
    public  Students(int age,String name) {
    	this.age = age;
    	this.name = name;
    }
	@Override//重写方法比较
	public int compare(Students arg0, Students arg1) {
		if(arg0.age > arg1.age) {
			return 1;
		}else if(arg0.age < arg1.age) {
			return -1;
		}else {
		return 0;
		}
	}
}
           

最后:同步控制

Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。

发际线不保,再见!