------- android培训、java培训、期待与您交流! ----------
Set:无序,不可以重复
|--HashSet:数据结构是哈希表,线程是非同步的
保证元素唯一性的原理:判断元素的hashCode值是否相同。如果相同,还会继续判断元素的equals方法,是否为true。
|--TreeSet:可以对Set集合中的元素进行排序
底层数据结构是二叉树
保证元素唯一性的依据:compareTo方法return 0.
只有HashSet才关联哈希值,TreeSet走的是二叉树。TreeSet是通过compareTo方法来确定元素是否相同。
如果想TreeSet集合删除元素或者判断元素是否包含,走的都是CompareTo方法return 0.但凡返回0,认为元素是相同的。
TreeSet排序的第一种方式:让元素自身具备比较性。
元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet的第二种排序方式
当元素自身不具备比较性,或者具备的比较性不是所需要的,此时就需要让集合自身具备比较性。
在集合一初始化时,就有了比较方式。
当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数
当两种排序都存在时,以比较器为主。
/*
练习:按照字符串长度排序
字符串本身具备比较性,但是它的比较方式不是所需要的
这时就只能使用比较器
*/
import java.util.*;
class TreeSetTest
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet(new StrLenComparator());
ts.add("abcd");
ts.add("cc");
ts.add("cba");
ts.add("aaa");
ts.add("z");
ts.add("hahahaha");
Iterator it = ts.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
class StrLenComparator implements Comparator
{
public int compare(Object o1,Object o2)
{
String s1 = (String)o1;
String s2 = (String)o2;
/*
if(s1.length()>s2.length())
return 1;
if(s1.length()==s2.length())
return 0;
*/
int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if(num==0)
return s1.compareTo(s2);
return num;
}
}
泛型:JDK1.5以后出现新特性,用于解决安全问题,是一个类型安全机制。
好处:
1、将运行时期出现的问题ClassCastException,转移到了编译时期方便于程序员解决问题,让运行时期问题减少,安全。
2、避免了强制转换的麻烦
泛型格式:通过<>来定义要操作的引用数据类型
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型,其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可
class GenericDemo
{
public static void main(String[] args)
{
ArrayList a1 = new ArrayList();
a1.add("abc01");
a1.add("abc0991");
a1.add("abc014");
//a1.add(4);//a1.add(new Integer(4));
Iterator it = a1.iterator();
while(it.hasNext())
{
String s = it.next();
System.out.println(s+":"+s.length());
}
}
}
泛型类定义的泛型,在整个类中有效。如果被方法使用。
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上
特殊之处:
静态方法不可以访问类上定义的泛型。
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上
注意:泛型定义在方法上,需要放到返回值类型的前面,修饰符的后面。
?通配符,也可以理解为占位符。
泛型的限定:【泛型的高级应用,泛型的复合应用】
? extends E:可以接收E类型或者E的子类型,上限。
? super E:可以接收E类型或者E的父类型,下限。
Map集合:该集合存储键值对,一对一对往里存,而且要保证键的唯一性
1,添加
put(K key,V value)
putAll(Map extends K,? extends V> m)
2,删除
clear()
remove(Object key)
3,判断
containsValue(Object value)
containsKey(Object key)
isEmpty()
4,获取
get(Object key)
size()
values()
entrySet()
keySet()
Map
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。
该集合是线程同步的。【JDK1.0】效率低
|--HashMap:底层是哈希表数据结构,允许使用null键和null值。
该集合是不同步的。【JDK1.2】效率高
|--TreeMap:底层是二叉树数据结构,线程不同步。可以用于给
Map集合中的键进行排序。
和Set很像
其实,Set底层就是使用了Map集合。
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
Map map = new HashMap();
//添加元素,当添加时出现相同的键,那么添加后的值会覆盖原有键
//对应的值,并put方法会返回被覆盖的值。
System.out.println("put:"+map.put("01","zhangsan1"));
System.out.println("put:"+map.put("01","wangwu"));
map.put("02","zhangsan2");
map.put("03","zhangsan3");
System.out.println("containsKey:"+map.containsKey("022"));
//System.out.println("remove:"+map.remove("02"));
System.out.println("get:"+map.get("023"));
map.put("04",null);
System.out.println("get:"+map.get("04"));
//可以通过get方法的返回值来判断一个键是否存在。
//通过返回null来判断。
//获取map集合中所有的值
Collection coll = map.values();
System.out.println(coll);
System.out.println(map);
}
}
map集合的两种取出方式
1、Set
所以可以迭代方式取出所有的键,再根据get方法,获取每一个键对应的值。
Map集合的取出原理:将map集合转成set集合,再通过迭代器取出。
2、Set
练习:
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数,希望打印结果:a(1)c(2)......
通过结构发现,每一个字母都有对应的次数,说明字母和次数之间都有映射关系
注意,当发现有映射关系时,可以选择map集合,因为map集合中存放的就是映射关系
什么时候使用map集合呢?
当数据之间存在映射关系时,就要先想map集合。
思路:
1,将字符串转换成字符数组,因为要对每一个字母进行操作
2,定义一个map集合,因为打印结果的字母有顺序,所以用treemap集合
3,遍历字符数组
将每一个字母作为键去查map集合
如果返回null,将该字母和1存入到map集合中
如返回不是null,说明该字母 在map集合中已经存在并有对应此时
那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到
map集合中,覆盖调用原理键所对应的值。
4,将map集合中的数据变成指定的字符串形式返回。
import java.util.*;
class MapTest3
{
public static void main(String[] args)
{
String s = charCount("aakb+fc-dabcd1ke,fa");
System.out.println(s);
}
public static String charCount(String str)
{
char[] chs = str.toCharArray();
TreeMap tm = new TreeMap();
int count = 0;
for(int x=0; x='a' && chs[x]<='z' || chs[x]>='A' && chs[x]<='Z'))
continue;
Integer value = tm.get(chs[x]);
if(value!=null)
count = value;
count++;
tm.put(chs[x],count);
count = 0;
/*
if(value==null)
{
tm.put(chs[x],1);
}
else
{
value = value + 1;
tm.put(chs[x],value);
}
*/
}
//System.out.println(tm);
StringBuilder sb = new StringBuilder();
Set> entrySet = tm.entrySet();
Iterator> it = entrySet.iterator();
while(it.hasNext())
{
Map.Entry me = it.next();
Character ch = me.getKey();
Integer value = me.getValue();
sb.append(ch+"("+value+")");
}
return sb.toString();
}
}
集合框架的工具类
Collections
比较对象大小要么compare要么compareTo
/*
Arrays:用于操作数组的工具类
里面都是静态方法
asList:将数组变成list集合
*/
import java.util.*;
class ArraysDemo
{
public static void main(String[] args)
{
//int[] arr = {2,4,5};
//System.out.println(Arrays.toString(arr));
String[] arr = {"abc","cc","kkkk"};
/*
问:把数组变成list集合有什么好处?
答:可以使用集合的思想和方法来操作数组中的元素
注意:将数组变成集合,不可以使用集合的增删方法。
因为数组的长度是固定的。
contains
get
indexOf()
subList()
如果增删,那么会发生UnsupportedOperationException
*/
List list = Arrays.asList(arr);
//sop("contains:"+list.contains("cc"));
//list.add("qq");//UnsupportedOperationException
//sop(list);
//int[] nums = {2,4,5};
Integer[] nums = {2,4,5};
Listli = Arrays.asList(nums);
/*
如果数组中的元素都是对象,那么变成集合时,数组中的元素就
直接转成集合中的元素。如果数组中的元素都是基本数据类型,
那么会将该数组作为集合中的元素存在。
*/
sop(li);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
/*
集合变数组
Collection接口中的toArray方法
*/
import java.util.*;
class CollectionToArray
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add("abc1");
al.add("abc2");
al.add("abc3");
/*
1、指定类型的数组到底要定义多长呢?
(1)当指定类型的数组长度小于了集合的size,那么该方法内部会
创建一个新的数组,长度为集合的size;
(2)当指定类型的数组长度大于了集合的size,就不会新创建数组,
而是使用传递进来的数组,所以创建一个刚刚好的数组最优。
2、为什么要将集合变数组?
为了限定对元素的操作。不需要进行增删了。
*/
String[] arr = al.toArray(new String[al.size()]);
sop(Arrays.toString(arr));
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
高级for循环
格式:
for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)
{
}
对集合进行遍历
只能获取集合中的元素,但是不能对集合进行操作。
迭代器除了遍历,还可以进行remove集合中元素的动作
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作
传统for和高级for有什么区别呢?
高级for有一个局限性,必须有被遍历的目标。
建议在遍历数组的时候,还是希望使用传统for,因为传统for可以定义角标。
/*
JDK1.5版本出现的新特性
方法的可变参数
在使用时注意:可变参数一定要定义在参数列表最后面
*/
class ParamMethodDemo
{
public static void main(String[] args)
{
/*
int[] arr = {3,4};
show(arr);
int[] arr1 = {2,3,4,5};
show(arr1);
*/
//虽然少定义了方法
//但是每次都要定义一个数组,作为实际参数。
show(2,4,5,6);
/*
可变参数,
其实就是上一种数组参数的简写形式
不用每一次都手动的建立数组对象
只要将要操作的元素作为参数传递即可
隐饰将这些参数封装成了数组
*/
}
public static void show(int... arr)
{
System.out.println(arr.length);
}
}
/*
StaticImport 静态导入
当类名重名时,需要指定具体的包名
当方法重名时,指定具备所属的对象或者类
*/
import java.util.*;
import static java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员
import static java.lang.System.*;//导入了System类中所有静态成员
class StaticImport extends Object
{
public static void main(String[] args)
{
out.println("haha");
int[] arr = {3,1,5};
sort(arr);
int index = binarySearch(arr,1);
System.out.println(Arrays.toString(arr));
System.out.println("Index="+index);
}
}