05.集合框架

1. Collection

1.1 List和set

在实际开发中,需要将使用的对象存储与特定数据结构的容器中。JDK提供了这样的容器——集合(Collection)。Collection是接口,定义了集合相关的操作方法,其有两个子接口:List与Set

  • List:可重复集
    在这里插入图片描述

1.2 集合持有对象的引用

集合中存储的都是引用类型元素,
并且集合只保存每个元素对象的引用,
而并非将元素对象本身存入集合。

在这里插入图片描述

public void testRef(){
	Collection <Cell>cells = new ArrayList<Cell>();
	cells.add(new Cell(1,2));
	Cell cell = new Cell(2,3);
	cells.add(cell);
	System.out.println(cell);	//(2,3)
	System.out.println(cells);	//[(1,2),(2,3)]
	
	cell.drop();
	System.out.println(cell);	//(3,3)
	System.out.println(cells)	//[(1,2),(3,3)]
}

1.3 add方法

  • Collection定义了一个add方法用于向集合中添加新元素。
  • 该方法会将给定的元素添加进集合,若添加成功则返回true,否则返回false。
public void testAdd(){
	Collection <String>c = new ArrayList<String>();
	System.out.println(c);	// []
	c.add(“a”);
	c.add(“b”);
	c.add(“c”);
	System.out.println(c);	//[a,b,c]
}

1.4 contains方法

  • boolean contains(Object o)

该方法会用于判定给定的元素是否被包含在集合中。若包含则返回true,否则返回false。
这里需要注意的是,集合在判断元素是否被包含在集合中是根据每个元素的equals()方法进行比较后的结果。
通常有必要重写equals()保证contains()方法的合理结果

public void testContains(){
	Collection <Cell> cells = new ArrayList<Cell>();
	cells.add(new Cell(1,2));
	cells.add(new Cell(1,3));
	cells.add(new Cell(2,2));	
	cells.add(new Cell(2,3));
	Cell cell = new Cell(1,3);
	//List集合contains方法和对象的equals方法相关
	boolean  flag = cells.contains(cell);
	//如果Cell不重写equals方法将为false
	System.out.println(flag); 	//true
}

1.5 Size,clear,isEmpty

int size()

该方法返回当前集合中的元素总数。

void clear()

该方法用于清空当前集合。

boolean isEmpty()

该方法用于判断当前集合中是否不包含任何元素

pulbic void testSizeAndClearAndEmpty(){
Collection<String>c = new HashSet<String>();
System.out.println(c.isEmpty()); 	//true
c.add("java");c.add("cpp");c.add("php");
c.add("c#");c.add("objective-c");
System.out.println("isEmpty:"+c.isEmpty()+",size:"+c.size())
//isEmpty:false,size:5
c.clear();
System.out.println("isEmpty:"+c.isEmpty()+",size:"+c.size());
//isEmpty:true,size:0
}

1.6 addAll,containsAll

boolean addAll(Collection<?extends E>c)

该方法需要我们传入一个集合,并将该集合中的所有元素添加到当前集合中。如果此collection由于调用而发生更改,则返回true

boolean containsAll(Colletion<?>c)

该方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回true。

public void testAddAndContainsAll(){
	Collection<String>c1 = new ArrayList<String>();
	c1.add(“java”); c1.add(“cpp”); c1.add(“php);
	c1.add(“c#”); c1.add(“objective-c”);
	System.out.println(c1);	 //[java,cpp,php,c#,objective-c]
	Collection<String> c2 = new HashSet<String>();
	c2.addAll(c1);
	System.out.println(c2); 	//[cpp,php,c#,java,objective-c]
	Collection<String>c3 = new ArrayList<String>();
	c3.add(“java”); 	c3.add(“cpp”);
	System.out.println(c1.containsAll(c3));		//true
}

2. Iterator

  • 迭代器用于遍历集合元素。获取迭代器可以使用Collection定义的方法:
    Iterator iterator()

迭代器Iterator是接口,集合在重写Collection的itrator()方法时利用内部类提供了迭代器的实现。

  • Iterator提供了统一的遍历集合元素的方式,其提供了用于遍历集合的两个方法:

boolean hasNext():判断集合是否还有元素可以遍历。
E next():返回迭代的下一个元素

2.1 hasNext、next方法

public void testHasNextAndNext(){
	Collection<String>c = new HashSet<String>();
	c.add("java");  c.add("cpp"); c.add("php");
	c.add("c#"); 	c.add("objective-c");
	Iterator<String>it = c.iterator();
	while(it.hasNext()){
		String str = it.next();
		System.out.println(str);
	}
}

2.2 remove方法

  • 在使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抛出并发更改异常。我们可以通过迭代器自身提供的remove()方法来删除通过next()迭代出来的元素。
    void remove();
  • 迭代的删除方法是在元集合中删除元素。
  • 这里需要注意的是,在调用remove方法前必须通过迭代器的next()方法迭代过元素,那么删除的就是这个元素。并且不能再次调用remove方法,除非再次调用next()后方可再次调用。
public void testRemove(){
Collection<String>c = new HashSet<String>();
c.add("java"); 	c.add("cpp");
c.add("php");		c.add("c#");
c.add("objective-c");
System.out.println(c);	//[cpp,php,c#,java,objective-c]
Iterator<String> it = c.iterator();
while(it.hasNext()){
	String str = it.next();
	if(str.indexOf(‘c’)!=-1){
		it.remove();//删除包含字母c的元素
	}
}	
System.out.println(c)	//[php,java]
}

3.增强型for循环

  • Java5.0之后推出了一个新的特性,增强for循环,也称之为新循环。该循环不同于传统循环的工作,其只用于遍历集合或数组。
  • 语法:
for(元素类型 e:集合或数组){
	循环体
}
  • 新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式。所以新循环本质上是迭代器。
public void testForeach(){
	Collection <String>c = new HashSet<Stirng>();
	c.add("Java");
	c.add("cpp");
	c.add("php");
	c.add("c#");
	c.add("objective-c");
	for(String str : c){
		System.out.print(str.toUpperase()+"");
	}
	//CPP PHP C# JAVA OBJECTIVE-C
}

4.泛型在集合中的应用

  • 泛型是java SE 5.0引入的特性,泛型的本质是参数化类型。在类、接口和方法的定义过程中,所操作的数据类型被传入的参数指定。

  • Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中元素的类型。Java编译器可以据此进行类型检查,这样可以减少代码在运行时出错的可能性。

  • ArrayList类的定义中,中的E为泛型参数,在创建对象时可以将类型作为参数传递,此时,类定义所有的E将被替换成传入的参数;

public class ArrayList<E>{
	… … …
	public boolean add(E e){};
	public E get(int index){};
}
ArrayList<String>list = new ArrayList<String>();
List.add("One");
List.add(100);

5.集合操作-线性表

5.1 Llist

  • ArrayList 和 LinkedList
    List接口是Collection的子接口,用于定义线性表数据结构。可以将List理解为存放对象的数组,只不过其元素个数可以动态的增加或减少。

  • List接口的两个常见类为ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。

  • 可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别。ArrayList适合于随机访问而LinkedList更适合于插入和删除。在性能要求不是特别苛刻的情形下可以忽略这个差别。
    在这里插入图片描述

5.2get和set

  • List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下标的get和set方法:

E get(int index)

获取集合中指定下标对应的元素,下标从0开始。

E set(int index,E elment)

将给定的元素存入给定位置,并将原位置的元素返回。

Public void testGetAndSet(){
	List<String>list  = new ArrayList<String>();
	list.add("java");	list.add("cpp"); list.add("php");
	list.add("c#");	list.add("objective-c");
	//get方法遍历List
	forint i= 0 ;i<list.size();i++){
		System.out.pritnln(list.get(i).toUpperCase());
	}
	String value = list.set(1,"c++");
	System.out.println(value);	//cpp
	list.set(1,list.set(3,list.get(1)));	//交换位置1和3上的元素
	System.out.println(list);	//[java,c#,php,c++,objective-c]
}

5.3 插入和删除

  • List根据下标的操作还支持插入与删除操作。

    void add(int index,E element);

    将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。

    E remove(int index)

    删除给定位置的元素,并将被删除的元素返回。

public void testInsertAndRemove(){
	List<String>list = new ArrayList<String>();
	list.add("java");
	list.add("c#");
	System.out.println(list);	//[java,c#]
	
	list.add(1,"cpp");
	System.out.println(list);	//[java,cpp,c#]
	
	list.remove(2);
	System.out.println(list);	//[java,cpp]
}

5.4 subList

  • List的subList方法用于获取子List
  • 需要注意的是subList获取的List与原List占有相同的存储空间,对子List的操作会影响原List。
  • List subList(int fromIndex,int toIndex);
  • fromIndex和toIndex是截取子List的首尾下标(前包括,后不包括)
    在这里插入图片描述
public void testSubList(){
	List<Integer>list = new ArrayList<Integer>();
	for(int i = 0; i<10;i++){
		list.add(i);
	}
	System.out.println(list);//[0,1,2,3,4,5,6,7,8,9]
	List<Integer>subList = list.subList(3,8);
	System.out.println(subList);//[3,4,5,6,7]
	//subList获得的List和源List占有相同的数据空间
	for(int I = 0 ;i<subList.size();i++){
		subList.set(i,subList.get(i)*10);
	}
	System.out.println(subList);//[30,40,50,60,70]
	System.out.println(list);//[0,1,2,30,40,50,60,70,8,9]
	list.subList(3,8).clear();//可以用于删除连续元素
	System.out.println(list);
}

5.5 List转换为数组

  • List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。

  • 其有俩个方法:
    Object[] toArray()
    T[] toArray(T[] a);

  • 其中第二个方法是比较常用的,我们可以传入一个指定的类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。

public  void testListToArray(){
	List<String>list = new ArrayList<String>();
	list.add("a");
	list.add("b");
	list.add("c");
	String[] strArr = list.toArray(new String[]{});
	//[a,b,c]
	System.out.println(Arrays.toString(strArr));
}

5.6 数组转换为List

  • Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。
  • 其方法定义为:
static<T> List<T> asList<T… a>
  • 返回的List的集合元素类型由传入的数组的元素类型决定。
  • 并且要注意的是,返回的集合我们不能对其增删元素,否则会抛出异常。并且对集合的元素进行修改会影响数组对应的元素。
public void testArrayToList(){
	String[] strArr = {"a","b","c"};
	List<String>list = Arrays.asList(StrArr);
	System.out.println(list);	//[a,b,c]
	//list.add("d");//会抛出UnsupporedOperationException
	
	//java.util.Arrays$ArraysList
	System.out.println(list.getClass().getName());
	List<String>list1 = new ArrayList<String>();
	list1.addAll(Arrays.asList(strArr));
}

5.7 List排序

  • Collections.sort方法实现排序
  • Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。
    该方法定义为:
void sort(List<T> list);

该方法的作用是对给定的集合元素进行自然排序。

  • Collections.sort方法实现排序
public void testSort(){
	List<Integer>list = new ArrayList<Integer>();
	Random r = new Random(1);
	for(int I = 0 ;i<10;i++){
		list.add(r.nextInt(100);
	}
	System.out.println(list); //[85,88,47,23,3,4,54,32,5,98]
	
	Collections.sort(list);//从小到大排好序
	System.out.println(list);
}
  • Comparable

Collections的sort方法是对集合元素进行自然排序,那么两个元素对象之间就一定要有大小之分。这个大小之分是如何界定的?实际上,在使用Collections的sort排序的集合元素都必须是Comparable接口的的实现类,该接口表示其子类是可比较的,因为实现该接口必须重写抽象方法:

Int compareTo(T t);

该方法用于使当前对象与给定对象进行比较。
若当前对象大于给定对象,那么返回值应为>0的整数。
若小于给定对象,那么返回值应为<0的整数。
若两个对象相等,则应返回0。

public void testComparable(){
	/* Cell实现了Comparable接口,compareTo方法逻辑为按照row值的大小排序*/
//public int compareTo(Cell o) {return this.row-o.row;}
	List<Cell>cells = new ArrayList<Cell>();
	cells.add(new Cell(2,3));
	cells.add(new Cell(5,1));
	cells.add(new Cell(3,2));
	Collections.sort(cells);
	System.out.println(cells);	//[(2,3),(3,2),(5,1)]
}
  • 一旦Java类实现了Comparable接口,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调(约定)的方式。
  • Comparator接口要求实现类必须重写其定义的方法:
  • 该方法的返回值要求:
    若o1>o2则返回值应>0;
    若o1>o2则返回值应<0;
    若o1==o2则返回值应为0
public void testComparator(){
List<Cell>cells =  new ArrayList<Cell>();
cells.add(new Cell(2,3));
cells.add(new Cell(5,1));
cells.add(new cell(3,2));
//按照col值的大小排序
Collections.sort(cells,new Comparator<Cell>(){
		public int compare(Cell o1,Cell o2){
			return o1.col – o2.col;
		}
	});
System.out.println(cells);//[(5,1),,(3,2),(2,3)]
}

6. 队列和栈

6.1 Queue

  • 队列(Queue)是常用的数据结构,可以将队列堪称特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。

  • 队列遵循先进先出(FIFO first input First Output)的原则。

  • JDK中提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常要进行添加和删除的操作,而LinkedList在这方面效率较高)。

  • Queue接口中主要方法如下:

boolean offer(E e);将一个对象添加至队尾,如果添加成功则返回true。
E poll();从队首删除并返回一个元素。
E peek();返回队首的元素(但并不删除)。
public  void testQueue(){
	Queue<String>queue = new LinkedList<String>();
	queue.offer("a");
	queue.offer("b");
	 queue.offer("c");
	System.out.println(queue);	//[a,b,c]
	String str = queue.peek();
	System.out.println(str);	// a
	while(queue.size()>0){
		str = queue.poll();
		System.out.println(str + ""); // a b c
	}
}

6.2 Deque

  • Deque是Queue的子接口,定义了所谓“双端队列”即从队列的两段分别可以入队(offer)和出对(poll),linkedList实现了该接口。
  • 如果将Deque限制为只能从一端入队和出队,则可实现“栈”(Stack)的数据结构,对于栈而言,入栈称之为push,出栈称之为pop。
  • 栈遵循先进后出(FILO First Input Last Output)的原则
    在这里插入图片描述
pulbic void testStack(){
	Deque<String>stack = new LinkedList<String>();
	statck.push("a");
	statck.push("b");
	statck.push("c");
	System.out.println(stack);	//[c,b,a]
	String str = stack.peek();
	System.out.println(str);	//c
	while(stack.size()>0){
		str = statck.pop();
		System.out.println(str+"");//c b a	}
}

7. Map接口

  • Map接口定义的集合又称查找表,用于存储所谓“Key-Value”映射对。Key可以堪称是Value的索引,作为Key的对象在集合中不可以重复。
  • 根据内部数据结构的不同,Map接口有多种实现类,其中常用的由内部为hash表实现的HashMap和内部为排序二叉树实现的TreeMap。

7.1 put()方法

  • Map接口中定义了向Map中存放元素的put方法:
  • 将Key-Value对存入Map,如果在集合中已经包含Key,则操作将替换该Key所对应的Value,返回值为该Key原来所对应的Value(如果没有则返回null)。
public void testPut(){
	//向map中添加元素
	employees.put(“张三”,new Emp(“张三”,25,
	 “男”,5000));
	employees.put(“李四”,new Emp(“李四”,21,“女”,6000));
}

7.2 get()方法

Map接口中定义了从Map中获取元素的get方法:
V get(Object key)
返回参数key所对应的Value对象,如果不存在则返回null.

public  void testGet(){
	//从Map中使用key获取value
	Emp emp = employees.get("张三");
	System.out.println(emp);
}

7.3 containsKey()方法

Map接口中定义了判断某个key是否在Map中存在:
boolean containsKey(Object key);
若Map中包含给定的key则返回true,否则返回false。

public  void testContainsKey(){
	boolean has = employees.containsKey("李四");
	System.out.println("是否有员工李四:"+has);
}

7.4 containsValue()方法

7.5 remove()方法

8. Hash表原理

在这里插入图片描述

8.1 hashCode方法

  • 从HashMap的原理中我们可以看到,key的hashCode()方法的返回值对HashMap存储元素时会起着很重要的作用。而HashCode()方法实际上是在Object中定义的。那么应当妥善重写该方法:

  • 对重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode方法(Object提供的hashCode方法将返回该对象所在内存地址的整数形式)。

  • 重写hashCode方法是需要注意两点:其一,与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值相同;其二、hashCode返回的数值应符合hashCode算法的要求,试想如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE(如Eclipse)提供的工具自动生成hashCode方法。

  • 重写hashCode方法

public void hashCode(){
	final int prime = 31;
	int result = 1;
	result = prime * result +age;
	result = prime * result +((gender == null) ? 0 : gender.hashCode());
	result = prime * result +((name == null) ? 0: name.hashCode());
	long temp;
	Temp = Double.doubleToLongBits(salary);
	result = prime * result +(int) (temp ^ (temp >>>32));
	return result;
}

8.2装载因子及HashMap优化

  • Capacity:容量,hash表里bucket(桶)的数量,也就是散列数组大小。
  • Initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量是16.也可以使用特定容量。
  • Size:大小,当前散列表中存储数据的数量。
  • Load factor:加载因子,默认值0.75(就是75%),当向散列表增加数据时如果size/capacity的值大于Load factor则发生扩展并且重新散列(rehash).
  • 性能优化:加载因子较小时,散列查找性能会提高,同时也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果。在创建散列表时指定合理容量,减少rehash提高性能。

8.3 Map遍历

  • Map提供了三种遍历方式:
    遍历所有的key
    遍历所有的key-value(键值对)对
    遍历所有的value(不常用)
8.3.1 遍历所有key的方法:
Set <K> keyset()

该方法会将当前Map中所有的key存入一个Set集合后返回。

/** 使用keySet遍历所有监测点*/
public  void testKeySet(){
	Set<String> keyset = map.keyset();
	for(String key: keyset){
		System.out.println(“监测点:”+key);
	}
}
8.3.2 使用entryset()方法

遍历所有的键值对的方法:

Set<Entry<K,V>> entrySet()

该方法会将当前Map中每一组key-value对封装为一个Entry对象并存入一个Set集合后返回。

/**	使用entrySet遍历所有检测点PM2.5最大值*/
public void testEntrySet(){
	Set<Entry<String,Integer>> entrySet = map.entrySet();
	for(Entry<String,Integer> entry : entrySet){
		System.out.println(entry.getKey()+":"+entry.getVlaue());
	}
}
8.3.3 values()

Collection values()
获取所有的value直意义不大,不常用。

9.有序Map

  • LinkedHashMap实现

使用Map接口的哈希表和链表实现,具有可预知的迭代顺序。此实现与HashMap的不同之处在于:
LinkedHashMap维护着一个双向循环链表。此链表定义了迭代顺序,该迭代顺序通常就是存放元素的顺序。
需要注意的是,如果在Map中重新存入已有的key,那么key的位置不会发生改变,只是将value值替换。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XYDrestart

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值