Java基础 自学讲义 6.Java集合框架

Java集合框架这个东西应该是类似于C++的STL标准库, 便于使用一些常见的数据结构:D

目录
一.Java集合框架

  1. 将接口和实现分离
  2. Collection接口
  3. 迭代器
  4. 泛型使用方法
  5. 集合框架中的接口

二. 具体的集合

  1. 链表LinkedList(List)
  2. 数组列表ArrayList(List)
  3. 散列集HashSet(Set)
  4. 树集TreeSet(SortedSet)
  5. 队列与双端队列Queue&Deque
  6. 优先级队列PriorityQueue

三. 映射(Map)

  1. 基本映射操作
  2. 更新映射值
  3. 映射视图
  4. 弱散列映射
  5. 链接散列集与映射
  6. 枚举集和映射
  7. 标识散列映射

四. 视图与包装器

  1. 轻量级集合包装器
  2. 子范围subrange
  3. 不可修改视图unmodifiable views
  4. 同步视图
  5. 受查视图

五. 算法

  1. 混乱与排序
  2. 二分查找
  3. 简单算法
  4. 批操作
  5. 集合与数组的转换
  6. 自定义算法
  7. 遗留的集合

一.Java集合框架

CoreJava中提到C++的STL提供了一种泛型算法, 现在想来的确是这样的, 还是挺好用的, 嘤嘤嘤;
本文中有一些地方出现了容器, 就是Java中集合的概念, 是我自己习惯说容器了, 呜呜;

1.将接口和实现分离

不要直接在一个类里面实现一个集合, 可以用一个支持接口的类来实现集合, 比如要实现一个队列, 既可以使用链表LinkedLIstQueue, 也可以使用循环列表CircularArrayQueue, 只需要implements queue就可以用不同的类实现一个队列接口了, 在定义一个队列的时候就可以先这样写一个接口:

然后这样去定义队列了:

Queue<People> humanity = new CircularArrayQueue<>(100);

表示定义一个长度为100的队列, 用循环列表来实现;

2.Collection接口

在Java中集合类的基本接口是Collection, 它提供了2个基本方法:
一个是add, 表示向集合中加入一个元素, 如果加入成功了就返回true, 否则返回false;
一个是Iterator迭代器, 用于遍历集合中的元素;

3.迭代器

迭代器就是Iterator;

可以用Iterator的next方法来遍历容器, 如果没有next就会返回一个NoSuchElementException错误, 所以调用next之前要用hasNext检查;
恕在下直言这笨比方法希望我不要多用, 尽量用for each来直接遍历容器把;
for-each循环可以和任何实现了iterable接口的对象一起工作;
在有了lambda表达式之后可以用forEachRemaining方法配合lambda表达式一起对容器内的元素做相同的操作;
在这里插入图片描

java中的iterator和C++中有一个巨大的不同:

所以在调用it.remove的时候, 是删除了上一个访问过的元素, 所以如果想要删除下一个迭代器的元素, 要先用it.next访问, 然后再remove掉;
如果在使用it.remove之前没有使用it.next, 会报错IllegalStateException
这样测试一下iterator:

import java.util.*;

class test12 {
	public static void main(String[] args) {
		Collection<String> a = new Vector<String>();
		a.add("GodV");
		a.add("We1less");
		for(Iterator<String>it = a.iterator();it.hasNext();){
			System.out.println((String)it.next());
		}
		
	}
}

要记得最好不要太早定义iterator, 可以等到用的时候再定义, 因为一旦执行Iterator<String>it = a.iterator()就会自动把迭代器初始化到容器的第一个位置;

4. 泛型使用方法

在Collection接口中, Java提供了很多有用的方法, 列举一些如下:



在我的测试中遇到了一些问题, 先上我写的MeVector测试代码:

import java.util.*;
import java.lang.reflect.*;

class test12 {
	public static void main(String[] args) {
		MyVector<String> me = new MyVector<String>();
		me.add("GodV");
		me.add("We1less");
		System.out.println(me.size());
		System.out.println(me.contains("GodV"));
		for(Iterator<String> it =me.iterator();it.hasNext();){
			System.out.println(it.next());
		}
		AbstractCollection<String> s = new Vector<>();
		Class<?> cl = s.getClass();
		Method[] Methods = cl.getDeclaredMethods();
		for(Method i : Methods){
			System.out.println(i);
		}
	}
}

class MyVector<E> extends AbstractCollection<E>{
	private Vector<E> vec;
	
	public MyVector(){
		vec = new Vector<E>();
	}

	@Override
	public int size(){
		return vec.size();
	}
	@Override
	public Iterator<E> iterator(){
		Iterator<E> it = vec.iterator();
		return it;
	}
	public boolean add(E e){
		return vec.add(e);
	}
}

//output:
//2
//true
//GodV
//We1less
//public synchronized boolean java.util.Vector.add(java.lang.Object)
//private void java.util.Vector.add(java.lang.Object,java.lang.Object[],int)
//public void java.util.Vector.add(int,java.lang.Object)
//public synchronized java.lang.Object java.util.Vector.remove(int)
//public boolean java.util.Vector.remove(java.lang.Object)
//......省略一些
//void java.util.Vector.checkInvariants()

首先, 如果不在MyVector中重写add方法, 就会出现unsupportedoperationexception异常, 仔细寻找错误后, 我用反射找到了AbstractCollection类中的方法, 发现add方法的返回值是void, 去查询文档中的add方法是这样的:

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

怪不得会出现这样的异常, 所以我重写了add方法;
但是其他的一些方法, 比如contains是可以正常使用的;

5. 集合框架中的接口

上一张图表示框架继承层次:
集合框架继承层次
随机访问

二. 具体的集合


所有的类都实现了Collection接口, 而以map结尾的类实现了Map接口;

1.链表LinkedList

链表的实现细节就不多说了, 谈谈Java中具体的链表的特点;
Collection中的iterator有next,hasNext和remove等一些方法, 但是没有add方法, previous这些方法;
因为Collection.iterator很通用, 所有对于某些无序集合比如set这些, 所以不能有add和previous这些方法, 因为不是按照下表索引序来的, 是无序的, 所以没有这些方法;
但是List是有序的, 所以现在有了ListIterator这个新的迭代器, 仅用于List, 有add, previous, hasPrevious方法, 就可以按顺序访问, 增加链表中的值, 还有get方法和set方法, 可以获取和修改指定迭代器位置的值;
使用add方法, 新加入的方法会被依次添加到迭代器当前位置之前;
要注意使用previous方法的时候, remove方法的效果和之前的有所不同, remove会删除之前操作过的那个变量, 而不是删除光标前面的那个变量;

在使用多个迭代器的时候要特别注意, 如果两个迭代器同时操作了当前的链表, 那么另一个迭代器的顺序就会发生改变, 容易出现问题, 所以CoreJava中建议, 同时最多使用一个迭代器做既读又写的操作, 其余的迭代器都只做读的工作;
Collection也有toString方法, 返回一个类似于[A, B, C, …]这样的字符串;
使用get方法, 以随机index的方法获取某个元素是效率极低的, 在Java中已经对get做了优化, 如果索引大于 size()/2 就从列表尾端开始搜索元素;
比较高效的方法是nextIndex和previousIndex方法来获取previous和next的元素的索引;
由于链表的随机访问元素效率比较低, 但是增删元素效率比较高;
所以如果经常需要随机访问元素最好就使用数组或者ArrayList;
随手测试一下:

import java.util.*;

class test13 {
	public static void main(String[] args) {
		List<String> me = new LinkedList<String>();
		me.add("GodV");
		me.add("We1less");
		me.add("weishen");
		me.add("mifengaaa");
		ListIterator<String> aIterator = me.listIterator();
		while(aIterator.hasNext()){
			System.out.println((String)aIterator.next());
		}
		System.out.println();
		aIterator.previous();aIterator.previous();
		aIterator.remove();
		aIterator = me.listIterator();
		while(aIterator.hasNext()){
			System.out.println((String)aIterator.next());
		}
	}
}
//output:
//GodV
//We1less
//weishen
//mifengaaa
//
//GodV
//We1less
//mifengaaa
//可见,remove会删除上一个遍历过的元素, 而不是像光标一样删除前面一个元素

上一些总结的常用的List和LinkedList的方法:

2.数组列表ArrayList

ArrayList应该是一个和Vector差不多的东西, 主要特点是可以动态改变大小, 区别在于:
Vector支持线程的同步, 同一时刻只有一个线程能够写Vector, 避免多线程同时写而引起的不一致性, 但实现同步需要很高的花费,所以它比ArrayList慢;
这个东西的详细使用前面写过了, 然后前面关于List的一些方法他也能用;
在这个博客里:Java基础 自学讲义 2. OOP部分特性 二.C.泛型数组列表

3.散列集HashSet

散列集, 散列表, 哈希表啥的都是这个东西;
书上讲的有点皮, 有点没看懂, 用一个比较好的散列函数是比较重要的;
这里贴一篇我以前C++里学散列表的博客, 原理都是一样的;
散列表随手测试一下:

import java.util.*;

class test14 {
	public static void main(String[] args) {
		Set<String> me =new HashSet<>();
		me.add("ZedKing");
		me.add("GodV");
		me.add("Godv");
		me.add("mifengaaa");
		me.add("mifengaaa");
		System.out.println(me.size());
		for(Iterator<String>it=me.iterator();it.hasNext();){
			System.out.println(it.next());
		}
	}
}
//output
//4
//GodV
//Godv
//ZedKing
//mifengaaa

Set是对大小写敏感的;
HashSet的toArray方法返回的是一个Object[]对象, 所以不能用一个ArrayList去接收;
因为在HashSet建立之前, 如果能对他的大概容量和容量使用率能有一个比较准确的预估, 可以大大提升这个哈希表的效率, 所以构造方法提供了以下几种:

最后一种只有在散列表的填充百分比大于loadFactor的百分比时, 散列表才会进行再散列(rehashed);

4. 树集TreeSet

TreeSet是由红黑树实现的(Red-Black Tree), 可以实现有序集合SortedSet
相比于普通Set就是可以实现有一定的顺序;
先上一个我的TreeSet测试代码:

import java.util.*;
import java.lang.reflect.*;

class test15 {
	public static void main(String[] args) {	
		SortedSet<item> aSet = new TreeSet<item>();
		aSet.add(new item("GodV",13));
		aSet.add(new item("mifengaaa",30));
		aSet.add(new item("Aluka",123));
		aSet.add(new item("Cpt",33));
		aSet.add(new item("GodV",42));
		System.out.println(aSet);
		NavigableSet<item> bSet = new TreeSet<>(
			Comparator.comparingInt(p->p.getNumber())
		);
//		像下面这样写也可以:
//		NavigableSet<item> bSet = new TreeSet<>(
//			Comparator.comparingInt(item::getNumber)
//		);
		bSet.addAll(aSet);
		System.out.println(bSet);

	}
}

class item implements Comparable<item>{
	private int number;
	private String description;
	
	public item(){}
	public item(String description, int number){
		this.number = number;
		this.description = description;
	}
	public int getNumber(){
		return this.number;
	}
	public String getDescription(){
		return this.description;
	}
	@Override
	public int compareTo(item u){
		return this.description.compareTo(u.getDescription())==0?Integer.compare(this.number, u.getNumber()):this.description.compareTo(u.getDescription());
	}
	@Override
	public String toString(){
		return this.description+"-"+this.number;
	}
}

//output
//[Aluka-123, Cpt-33, GodV-13, GodV-42, mifengaaa-30]
//[GodV-13, mifengaaa-30, Cpt-33, GodV-42, Aluka-123]

上面的代码中, 第一部分是我按照item类implement的Comparable接口, 重写了compareTo方法, 按照description排序的, 次关键词number排序, 所以在TreeSet中是直接按照Comparable接口实现的compareTo的方法排序的;
第二部分我查看了TreeSet的Constructor

		TreeSet<item> a = new TreeSet<>();
		Class<?> cl = a.getClass();
		Constructor<?>[] methods = cl.getDeclaredConstructors();
		for(Constructor<?> i : methods)	System.out.println(i);	

发现有下面几种构造器:

public java.util.TreeSet(java.util.SortedSet)
public java.util.TreeSet(java.util.Collection)
public java.util.TreeSet(java.util.Comparator)
public java.util.TreeSet()
java.util.TreeSet(java.util.NavigableMap)

所以也可以使用lambda表达式或者使用method reference传入一个Comparator来实现排序的指定, 这样就不会默认去调用自己的compareTo方法了;
也可以传入一个SortedSet或者Collection来Copy所有的内容;
最后上一些树集常用的方法:

也可以在这里看到:
极客学院TreeSet
还有最后一个问题, 上面我们使用到了三个接口, TreeSet, SortedSet和 NavigableSet; 这三个接口主要是支持的方法有些不一样, 需要的时候酌情选用把…

5. 队列与双端队列

ArrayDeque和LinkedList都实现了deque;
要注意的是接口也对deque提供了push pop方法, 但是我觉得最好不要用, 因为容易分不清前面和后面, 最好用addFirst addLast offerFirst offerLast和removeFirst removeLast pollFirst pollLast, 比较好区分;
给一些常用的方法:

6. 优先级队列

Java中的优先队列是用堆(heap)实现的(最小堆, 小顶堆), 当然也可以用二叉搜索树实现;

堆和二叉搜索树的区别是:
堆一定是完全二叉树, 二叉搜索树不一定是完全二叉树

名字叫PriorityQueue
有一些方法, 比如poll offer peek这种 还有isEmpty这些, 用到自然就知道了…
还有一个好像更牛逼的东西叫PriorityBlockingQueue优先级阻塞队列, 可用于并发?

三.映射

Java里的映射就是map, 提供一种 键-值 的数据结构

1.基本映射操作

Map主要有两种, HashMap和TreeMap, 一个无序一个有序的, HashMap稍微快一点;
有put, get, remove, forEach这些常见的方法, 要注意的是遍历Map的方法, 我总结了4种, CoreJava中给出的是第一种使用forEach+lambda表达式, 我认为这种方法应该是最好的;
遍历Map:
第一种可以使用Map的forEach方法加上Java8的lambda表达式:

aMap.forEach( (k,v)->{System.out.println(k+" "+v);} );

第二种可以使用Map.Entry来遍历Map的条目:

for(Map.Entry<String, String> it : aMap.entrySet()){
	System.out.println(it.getKey()+"="+it.getValue());
	System.out.println(it);
}

第三种可以使用for结合Map的keySet和values方法来遍历:

for(String a : aMap.keySet()){
	System.out.println(a);
}
for(String a : aMap.values()){
	System.out.println(a);
}

第四种是使用迭代器, 这种是看起来比较熟悉而且效率挺高的, 但是要注意, 不能在使用for循环访问迭代器的同时使用remove操作, javadoc说这样会发生不可预期的错误, 如果希望迭代的同时删除元素, 可以使用while来遍历:

for(Iterator<Map.Entry<String, String>> it = aMap.entrySet().iterator();it.hasNext();){
	Map.Entry<String, String> itt = it.next();
	System.out.println(itt.getKey()+"="+itt.getValue());
	System.out.println(itt);
}

当然还有第五种是在遍历keySet的时候调用get方法获取对应的值, 但是这种方法太捞了, 效率很低, 不提了, 就上一段测试代码吧:

for(String i : aMap.keySet()){
	System.out.println(i+"="+aMap.get(i));
}

测试代码如下:

import java.util.*;

class test16 {
	public static void main(String[] args) {
		Map<String,String> aMap = new TreeMap<>();
		aMap.put("Aluka", "AluWife");
		aMap.put("GodV", "mifengaaa");
		aMap.put("zz", "lym");
		aMap.forEach( (k,v)->{System.out.println(k+" "+v);} );
		System.out.println();
		aMap.remove("zz");
		for(Map.Entry<String, String> it : aMap.entrySet()){
			System.out.println(it.getKey()+"="+it.getValue());
			System.out.println(it);
		}
		System.out.println();
		for(String a : aMap.keySet()){
			System.out.println(a);
		}
		for(String a : aMap.values()){
			System.out.println(a);
		}
		System.out.println();
		for(Iterator<Map.Entry<String, String>> it = aMap.entrySet().iterator();it.hasNext();){
			Map.Entry<String, String> itt = it.next();
			System.out.println(itt.getKey()+"="+itt.getValue());
			System.out.println(itt);
		}
	}
}

输出如下:

Aluka AluWife
GodV mifengaaa
zz lym

Aluka=AluWife
Aluka=AluWife
GodV=mifengaaa
GodV=mifengaaa

Aluka
GodV
AluWife
mifengaaa

Aluka=AluWife
Aluka=AluWife
GodV=mifengaaa
GodV=mifengaaa

贴一些map和sortedmap的常用方法:

2.更新映射值

在Map中更新一对键值的方法啊, 比如我要用map来统计一篇文章里每个单词出现的次数, 可以这样写:
直接用put更新, 会自动顶替原来的键值;

Map<String,Integer> aMap = new HashMap<>();
//...输入一个单词
aMap.put(word, aMap.get(word)+1);

但是这样在第一次加入单词的时候使用get方法会获取到一个null, 所以可以用getOrDefault方法:

aMap.put(word, aMap.getOrDefault(word, 0)+1);

也可以用一个新的方法, 叫putIfAbsent方法:

aMap.putIfAbsent(word, 0);
aMap.put(word, aMap.get(word)+1);

然后CoreJava中最后给了一种最简单的, 是merge方法, 这个方法会在键值不存在的时候把word和1绑定为一对键值存入, 如果键值存在就会调用Integer.sum方法把word的值和1求和存入更新:

aMap.merge(word, 1, Integer::sum);

感觉这个merge的真正用法不应该是在这种场合… 可能在其他场合会有妙用把, 还有一些看起来烦的一比的更新键值的方法我就不贴图了0 0

3.映射视图

Map有三个视图, 分别是KeySet, values, entrySet;

KeySet是一个Set, 但不是HashSet或TreeSet;
values是一个实现了Collection的类;
entrySet也是一个Set, 里面存着键值对应条目;
可以这样使用它们(前面遍历的时候已用过了):

4.弱散列映射

WeakHashMap, 垃圾回收器会在认为某个键值不再使用的时候把它删掉;
下面是从别的博客里抄来的一段话, 不太理解, 以后再说吧:

当WeakHashMap某个键不再正常使用时,会被从WeakHashMap自动删除。更精确的说,对于一个给定的键,其映射的存在并不能阻止垃圾回收器对该键的丢弃,这就使该键称为被终止的,被终止,然后被回收,这样,这就可以认为该键值对应该被WeakHashMap删除。

5.链接散列集与映射

LinkedHashMap和LinkedHashSet, 与HashMap, HashSet的区别就是他们会保存自己插入时的顺序;

6. 枚举集和映射

EnumSet和EnumMap是是一个以枚举型Enum对象为键值的元素集;


关于这个部分的枚举集和映射的常用方法在下一小节里面贴;

7.标识散列映射

普通的Hash表都是用Object.hashCode来生成散列值的, IdentityHashMap不是这么做的, 他是用System.identityHashCode, 这个哈希值是根据对象的内存地址生成的, 所以比较IdentityHashMap的时候要用== 不要用equals;

四.视图与包装器

视图(views)的定义:

1.轻量级集合包装器

比如可以用Arrays.asList方法, 用Collections.singleton方法, Collections.nCopies方法, Collections.emptyList方法
这些方法获得的都是List的一个视图, 不能调用add, remove这些方法, 但是可以调用get和set, indexof这些方法;
查看其getClass类型, 可以看到比如asList返回的类型是:

class java.util.Arrays$ArrayList

要注意的是, java.util里的ArrayList和Arrays类里的ArrayList是不同的, 在Arrays.ArrayList里面实现add方法的时候是直接抛出一个UnsupportedOperationException异常的, 所以不能直接调用add方法, 一旦设定了capacity就不能改变了;
其他两个方法大概记一下, 就是singleton就是创建单个对象的视图, 然后nCopies是把视图中的每一个元素都视为一个对象, 然后Copy n个, 然后emptyList就是建立一个空的视图, 可以自己试试;

2.子范围subrange

可以对视图采用List subStaff = staff.subList(10, 20)这样来获取一个子范围, 也可以用subStaff.clear()来清空一个子范围;
对于Set, 获取子范围的根据不是下表索引序, 可以采用排序顺序, 对SortedSet同理;

3.不可修改的视图unmodifiable views

如果创建了一个视图, 那么是可以通过改变视图来影响原来的对象的, 如果使用unmodifiable views就可以只读取原来的对象的视图, 而不改变, 对所有的get方法都可以正常调用, 对所有的set方法都会抛出一个UnsupportedOperationException异常;

4.同步视图

可以创建一个同步视图, 这样就可以多线程同步访问了,像get和put这类方法都是同步操作:

Map<Integer, String> aMap = Collections.synchronizedMap(new HashMap<>());
aMap.put(3,"lanlan");
for(Iterator<Map.Entry<Integer, String>>it=aMap.entrySet().iterator();it.hasNext();){
	System.out.println(it.next());
}
5.受查视图

看下面这个代码:

        ArrayList bList = new ArrayList();
        bList.add(10);
        bList.add("Aluka");
        bList.add(new Date());
        for(Object i : bList){
            System.out.println(i);
        }

这个是很正常的一段不指定类型的ArrayList泛型吗然后在for-each循环中利用多态和动态绑定来打印List的内容;
但是如果像下面这样写:

List<String> aList = new ArrayList<>();
List bList = aList;
bList.add(10);

这样也是不会报错的, 但是有一个隐患, 在于List的泛型已经制定了类型为String, 但是传入了一个Integer类型的值, 在调用get()的时候就会出现类型不匹配的问题;
这样加入一个不匹配的类型到List中是很危险的,为了避免出现这种危险的行为, 使用受查视图checkedList就可以提前, 在加入到List 的时候就报出一个异常;

List<String> list = new ArrayList<>();
List<String> aList = Collections.checkedList(list,String.class);
aList.add("aluka");
aList.add(new Date());

上面的代码在运行到aList.add("aluka");都是没问题的, 在运行最后一行的时候, 会报错
incompatible types: java.util.Date cannot be converted to java.lang.String
然后惯例上一些常用方法图

五. 算法

Java提供了一些有用的算法模板

1.混乱与排序

Collections接口提供了shuffle方法和sort方法, shuffle用于给一个列表打乱顺序, sort会给一个列表排序;
要能实现这个shuffle或者sort方法必须要当前的list能实现set, add和remove方法;
shuffle示例:

String[] s = {"GodV","mifengaaa","17shou","Aluka","Gucun","Cpt"};
for(String i : s) System.out.println(i);
System.out.println();
Random rnd = new Random();
Collections.shuffle(Arrays.asList(s),rnd);
for(String i : s) System.out.println(i);

shuffle传入一个列表和一个Random对象, 用于确定随机乱序的随机数, 如果列表中的对象实现了RandomAccess接口, 也可以省略Random参数, RandomAccess接口内部实现其实啥也没有, 是一个标记接口, 详细可以看这个博客:RandomAccess 接口使用;
可以用a instanceof RandomAccess



sort示例如下, 详见我这个博客:Java8中排序算法比较器的三种写法(使用lambda表达式实现Comparator比较器)

String[] s = {"GodV","mifengaaa","17shou","Aluka","Gucun","Cpt"};
Collections.sort(Arrays.asList(s), Comparator.comparingInt(String::length));
Collections.sort(Arrays.asList(s), Comparator.comparingInt(str->(str.length())));
Collections.sort(Arrays.asList(s), (p,q)->{
    return Integer.valueOf(p.length()).compareTo(q.length());
});
Collections.sort(Arrays.asList(s), (p,q)->{
    return p.length()-q.length();
});
Collections.sort(Arrays.asList(s), new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.length()-o2.length();
    }
});
System.out.println();
Arrays.stream(s).forEach(System.out::println);

sort方法传入一个list和一个Comparator比较器, 如果list中的对象实现了Comparable接口, 也可省略Comparator参数;
如果想要用GodV的Comparator比较器排序, 可以用reversed或者reverseOrder()来实现:

Collections.sort(Arrays.asList(s), Comparator.comparingInt(String::length).reversed());
Arrays.asList(s).sort(Collections.reverseOrder(String::compareTo));

2.二分查找

可以用Collections.BinarySearch来做二分查找, 一定要记得在二分查找之前一定要进行sort操作, 传入的list必须是排过序的list, 测试:

String[] s = {"GodV","mifengaaa","17shou","Aluka","Gucun","Cpt"};
Collections.sort(Arrays.asList(s));
Arrays.stream(s).forEach(System.out::println);
System.out.println(Collections.binarySearch(Arrays.asList(s), "mifengaaa"));

如果找到元素, 就返回这个元素第一次出现的位置, 如果这个元素在list中没有找到就返回一个i使 − i − 1 -i-1 i1为这个元素应该在的正确位置;

3.简单算法


4.批操作

直接用集合提供的批操作来处理集合, 比如removeAll删除所有元素, retainAll删除所有重复元素, addAll增加全部元素, 之类的;

5. 集合与数组的转换

一个数组要转换成集合可以使用asList方法:

String[] s = {"Aluka"};
List<String> aList = Arrays.asList(s);

如果要集合转换成数组可以用toArray方法这样做:

Object[] aArray = aList.toArray();

但是缺点是使用toArray方法返回的数组中的每一个元素都是一个Object;
当然, 可以这样使用toArray的另一个用法来获得一个指定类型的数组:

//下面两种是等价的
String[] aStringArray = aList.toArray(new String[0]);
String[] aStringArray = aList.toArray(new String[aList.size()]);

传入toArray的对象可以是一个新建的大小为0的数组, 也可以直接定义好数组的长度;

6.自定义算法

CoreJava建议在写自己的算法的时候, 能用通用集合接口的地方尽量用通用的以提高程序的可用性;
比如算法中如果你希望用到ArrayList并且使用它的add, remove, size这些List也有的方法, 那你就可以直接用List而不是用ArrayList, 这样所有的List和ArrayList都可以使用这个算法了;

7.遗留的集合

1.Hashtable类基本等同于HashMap, 但是Hashtable支持并发访问, 应该尽量使用HashMap, 如果需要并发可以使用ConcurrentHashMap;
2.枚举Enumeration
3.属性映射property map
4.栈stack (push pop peek[就是top]方法)
5.位集BitSet

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值