Java集合操作 遍歷list並轉map


本文主要介紹如何使用java對list進行遍歷,並將list轉換成map。主要涉及jdk8的stream操作和guava工具包操作集合,並涉及到guava工具包的實現說明。

需求

有一個student list集合,包含學號,身高,體重,需要遍歷list,計算出BMI值,並將其轉換成 學號 -> student 的map集合。

Student.java

class Student{
    private int id;
    private float weight;
    private float height;
    private float IMB;
}
計算IMB值

Private Student calIMB(Student stu){
    int imb = stu.getWeight() / (stu.getHeight() * stu.getHeight());
    return stu.setIMB(imb);
}

jdk8 流式操作

流式操作的代碼非常簡潔,一行代碼即可搞定。

List<Student> studentList;
Map resultMap = studentList.stream().
map(stu -> calIMB(stu)).
collection(Collectors.toMap(stu::getId,stu -> stu,(s1,s2)-> s1));
  • Stream.map() 集合內的所有元素會被當成入參,運行一次入參方法。
  • Collectors.toMap(stu::getId,stu -> stu,(s1,s2)-> s1) 第一個參數是返回key,第二個參數是map對應的值,第三個參數為如果key重復時,選擇哪個作為map的value。

guava 工具類 Lists Maps

在工作中會遇到java7或者更低版本的環境,無法使用stream流,但是for循環又太繁瑣,可讀性又差,這時候可以嘗試使用google的guava工具包,guava提供了便捷的集合操作工具類,來看看如何實現這個需求呢。

List<Student> studentList;
studentList = Lists.transform(studentList, new Function<Student, Student>() {
    @Nullable @Override public Student apply(@Nullable Student stu) {
        return calIMB(stu);
    }
}); //計算IMB值
Map resultMap = Maps.uniqueIndex(studentList, new Function<Student, Integer>() {
    @Nullable @Override public Integer apply(@Nullable Student input) {
        return input.getId();
    }
}); //list轉map

利用Lists.transform對list進行封裝,使每個元素獲取時都會執行第二個參數自定義function。再用Maps.uniqueIndex()將list轉換成map,function的返回值就是map的key。

此處需要注意的是,Lists.transform並不會執行function,返回的只是guava封裝的TransformingRandomAccessList或TransformingSequentialList,在調用get或者Iterator獲取元素時,才會對獲取的元素執行function方法。

Lists.transform 源碼

下面看一下Lists.transform做了些什么

public static <F, T> List<T> transform(
    List<F> fromList, Function<? super F, ? extends T> function) {
return (fromList instanceof RandomAccess)
    ? new TransformingRandomAccessList<F, T>(fromList, function)
    : new TransformingSequentialList<F, T>(fromList, function);
}

先判斷list是否繼承RandomAccess接口,RandomAccess是一個聲明式接口,繼承RandomAccess代表支持非順序的獲取元素,比如ArrayList就繼承該接口,ArrayList.get(i)可以直接獲取到第i個元素,而鏈表就需要i次迭代才可獲取到數據。按RandomAccess的文檔說明,for循環直接get元素進行迭代要比迭代器的操作更快一些,這點令我無法理解,理論上ArrayList的迭代耗時應該基本一致才對,也不知道有什么形式的列表滿足這一含義。

下面以TransformingRandomAccessList為例來看后續實現:

private static class TransformingRandomAccessList<F, T>
      extends AbstractList<T> implements RandomAccess, Serializable {
final List<F> fromList;
final Function<? super F, ? extends T> function;

TransformingRandomAccessList(
    List<F> fromList, Function<? super F, ? extends T> function) {
    this.fromList = checkNotNull(fromList);
    this.function = checkNotNull(function);
}
@Override public void clear() {
    fromList.clear();
}
@Override public T get(int index) {
    return function.apply(fromList.get(index));
}
@Override public Iterator<T> iterator() {
      return listIterator();
    }
    @Override public ListIterator<T> listIterator(int index) {
      return new TransformedListIterator<F, T>(fromList.listIterator(index)) {
        @Override
        T transform(F from) {
          return function.apply(from);
        }
      };
    }
...
}

TransformingRandomAccessList的構造方法只記錄了list和function,並未進行其他操作,在調用get()方法或迭代器獲取元素時,會調用function.apply()方法,多次獲取同一個元素,自定義function會被重復調用多次。

那么guava包內是否有方法可以類似stream流進行轉換的時候就執行function獲取結果,而不是獲取元素時再執行的方法呢?

FluentIterable

FluentIterable 是guava集合類中常用的一個類,主要用於過濾、轉換集合中的數據,它的使用更像是stream流:

List<Student> studentList;
Map resultMap = FluentIterable.from(studentList).transform(new Function<Student, Student>() {
    @Nullable @Override public Student apply(@Nullable Student input) {
        return calIMB(input);
    }
}).uniqueIndex(studentList, new Function<Student, Integer>() {
    @Nullable @Override public Integer apply(@Nullable Student input) {
        return input.getId();
    }
});

和Lists、Maps工具類不同的是,每次transform操作都會對集合進行迭代,執行function,並將迭代后的Iterable通過from再次轉換成FluentIterable,用以支持鏈式調用,看起來很像套娃,用起來也非常簡潔。

小結

  • 迭代集合進行轉換操作,jdk8及以上版本用stream和lambda寫起來輕松愉快。
  • 如果是低版本的,則可以考慮使用FluentIterable,代碼也非常工整。
  • 若轉換操作非常耗時,不會重復獲取某個元素,並且只會使用集合的部分數據,使用Lists.transform進行轉換也有助於性能提升。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM