簡體   English   中英

在Java中使用List

[英]Cons'ing a List in Java

說我有一個java.util.List list ,我想創建一個新的List中加入元素e到年初list (即我想利弊 elist )。 例如,如果list

[1,2,3,4]

並且e5 ,那么cons(e,list)將是

[5,1,2,3,4]

listcons(e,list)的元素可以共享,但不應修改list

實施cons的最簡單和/或最有效的方法是什么? 結果不可修改是可以的。 允許使用Google Collections Library。

如果listcom.google.common.collect.ImmutableList怎么辦?

public static<T> List<T> cons(List<T> list, T t) {
    ArrayList<T> result = new ArrayList<T>(list);
    result.add(0, t);
    return result;
}

編輯回應評論:由於問題是“最簡單和/或最有效的方式來實施利弊”,我選擇了“最簡單”。 我不會驚訝地發現有更有效的方法。 將元素放在列表之前是另一種有效的方法,並且最初分配正確的大小可能會提高性能。 過早優化是萬惡之源。

Clojure提供了那種Lisp-y的東西。 雖然大多數人都認為使用Clojure語言(就像我一樣),但Clojure庫都是真正的Java代碼,如果你願意,你可以使用Java中的數據結構作為一個特殊的庫。 通過這種方式,您可以獲得利弊等等,並獲得Clojure使用的不變性。 Clojure數據strcutres也實現了等效的Java類型。

只是從不同的方向思考。

我相信你真正想要的答案是這樣的:

http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/List.html

該方法甚至被稱為cons

我沒有這個庫的經驗。 前幾天聽說過它。 我希望這很好!

一個有效的解決方案是創建自己的List實現,懶惰委托。 如果不可修改,那就不會太麻煩了。 如果通過工廠方法創建,那么您甚至可以使用兩個不同的底層實現之一,具體取決於參數List是否實現RandomAccess。

對於最簡單的情況(沒有檢查是否編譯,沒有健全性檢查等):

class ConsList<E> extends AbstractList<E>
{
    private final E first;
    private final List<E> rest;

    ConsList( E first, List<E> rest )
    {
        this.first = first;
        this.rest = rest;
    }

    public int get( int index )
    {
        return (index == 0) ? first : rest.get( index - 1 );
    }

    public int size()
    {
        return rest.size() + 1;
    }
}

我確信其他一些方法可以提高效率,而通過擴展AbstractSequentialList可以更好地滿足順序情況。

這不是眾所周知的,但Sun javac編譯器API包含一個不可變的List實現,它使用append()prepend()等方法返回新的不可變列表。 要使用它,您需要在類路徑上使用tools.jar ,並且需要以下import語句:

import com.sun.tools.javac.util.List;

示例代碼:

final List<Integer> list = List.of(6, 7);
System.out.println(list);

final List<Integer> newList =
    list.prepend(5)                       // this is a new List
        .prependList(List.of(1, 2, 3, 4)) // and another new List
        .append(8)                        // and another
        .reverse();                       // and yet another
System.out.println(newList);

System.out.println(list == newList);

System.out.println(list.getClass());
System.out.println(newList.getClass());

輸出:

6,7
8,7,6,5,4,3,2,1

class com.sun.tools.javac.util.List
class com.sun.tools.javac.util.List

我知道這是一個內部API,不應該在生產中使用,但它非常有趣(最后一個感覺正確的java集合)。

注意:顯然, CollectionList接口中的所有標准可變方法(如add() addAll() )都會拋出UnsupportedOperationException

參考:

因為你提到了cons函數,我將假設你正在使用由cons單元組成的鏈表的概念模型來解決這個問題。 具體來說,我假設您正在考慮每個列表都有一個car (第一個元素)和一個cdr (包含所有后續元素的子列表)。

Java支持鏈表作為java.util.LinkedList 這些對於線性遍歷是有益的,並且可以非常有效地插入元素。 這些與我上面提到的鏈表最相似。

Java還提供java.util.ArrayList 這種列表適用於隨機訪問,但插入元素時可能會很慢。 實際上,當在列表的開頭插入元素時,它們是最慢的。 因為ArrayList在幕后實現為數組,所以每個元素必須在列表中向前復制一個位置,以便為新的第一個元素騰出空間。 現在,如果您還需要更大的數組, ArrayList將分配一個新數組,復制所有元素,依此類推。

(谷歌的ImmutableList被稱為“隨機訪問”,所以它可能更接近后者。)

如果您打算經常使用cons方法,我建議將其與鏈表一起使用。 cons操作實質上是在列表的開頭添加元素。 這對於諸如陣列的線性結構沒有多大意義。 出於這個原因,我建議不要使用數組列表:它們在概念上對於這項工作是錯誤的。

非常挑剔:因為每次調用cons都會返回一個新列表,無論列表是LinkedList還是ArrayList ,都必須進行復制。 但是, cons操作的整個原理是它在鏈表上運行。

public <E> LinkedList<E> cons(E car, List<E> cdr) {
    LinkedList<E> destination = new LinkedList<E>(cdr);
    destination.addFirst(car);
    return destination;
}

請注意,上面的代碼是在閱讀上述答案后編寫的,所以我為任何偶然的抄襲道歉。 讓我知道你是否看到它,我會正確地承認。

假設您對返回LinkedList感到滿意,可以在此示例中使用ImmutableList作為cdr

你能使用CompositeCollection嗎?

public Collection cons(Collection c1, Collection c2)
{
    CompositeCollection cons = new CompositeCollection();
    cons.addComposited(c1);
    cons.addComposited(c2);
    return cons;
}

這不受其中一個參數是否是不可變的影響,並且仍然由原始集合c1和c2支持。

如果你需要一個List我可能會做以下事情:

public List cons(Collection c1, Collection c2)
{
    ArrayList cons = new ArrayList(c1.size() + c2.size());
    cons.addAll(c1);
    cons.addAll(c2);
    return cons;
}

我打算花2美分,然后看看是否有人想出更優雅的東西。 在一般情況下:

<E> List<E> cons(E e, List<E> list) {
    List<E> res = Lists.newArrayListWithCapacity(list.size() + 1);
    res.add(e);
    res.addAll(list);
    return res;
}

使用ImmutableList (不知道這有多高效):

<E> ImmutableList<E> cons(E e, ImmutableList<E> list) {
    return ImmutableList.<E>builder()
                        .add(e)
                        .addAll(list)
                        .build();
}

當然,LinkedList是在列表頭部插入項目的最有效方式嗎?

只需使用Java附帶的LinkedList

另一種變化,如果你只有Iterable。

public static <E> List<E> cons(E e, Iterable<E> iter) {
   List<E> list = new ArrayList<E>();
   list.add(e);
   for(E e2: iter) list.add(e2);
   return list;
}

public static <E> List<E> cons(Iterable<E>... iters) {
   List<E> list = new ArrayList<E>();
   for(Iterable<E> iter: iters) for(E e1: iter1) list.add(e1);
   return list;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM