2013-11-14 4 views
2

Я недавно начал просматривать коллекции GUAVA, а именно ImmutableList, и это кажется довольно громоздким (с экземпляром компоновщика и т. Д.) Есть ли библиотека, которая бы имитировала более «естественный» способ поведения групп (например, scala's http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List). Я хотел бы что-то, что позволяет добавлять/удалять и т. Д., Но сохраняя неизменность и, возможно, с целью выполнения, повторно использовать части старого списка.Неизменяемые коллекции в Java

+2

_I хотел что-то, что позволяет добавление/удаление и т.д., но сохраняя immutability_ противоречие. По определению вы не можете добавить или удалить из неизменяемого списка. – gla3dr

+0

Я не понимаю, как ImmutableList может позволить добавлять/удалять. Что не так с использованием обычных ArrayLists и с использованием ImmutableList.copyOf(), когда вы хотите иметь неизменяемую копию? –

+2

Поскольку вы знаете, что хотите, создайте свою собственную «неизменную» библиотеку коллекций. –

ответ

1

Если вы правильно поняли, вы хотите иметь неизменный список, который имеет удобные методы добавления/удаления, которые возвращают новые экземпляры списка, которые будут использовать как можно больше исходной структуры списка. Вы могли бы сделать что-то вроде этого:

public abstract class ImmutableList<T> implements Iterable<T> { 
    /** 
    * Adds an element to the head of the list, returning the new list. 
    * 
    * @param o The element to be added to the list. 
    * @return The list consisting of the element <var>o</var> followed by 
    *   this list. 
    */ 
    public final ImmutableList<T> add(final T o) { 
     return new Node<>(o, this); 
    } 

    /** 
    * Removes the element <var>o</var> resulting in a new list which 
    * is returned to the caller. 
    * 
    * @param o The object to be removed from the list. 
    * @return A list consisting of this list with object <var>o</var> removed. 
    */ 
    public abstract ImmutableList<T> remove(final T o); 

    public abstract boolean isEmpty(); 
    public abstract int size(); 

    public abstract boolean contains(final T o); 

    private ImmutableList() {} 

    /** 
    * Returns a "standard" enumeration over the elements of the list. 
    */ 
    public Iterator<T> iterator() { 
     return new NodeIterator<>(this); 
    } 

    /** 
    * The empty list. Variables of type ImmutableList should be 
    * initialised to this value to create new empty lists. 
    */ 
    private static final ImmutableList<?> EMPTY = new ImmutableList<Object>() { 
     @Override 
     public ImmutableList<Object> remove(final Object o) { 
      return this; 
     } 

     @Override 
     public boolean isEmpty() { 
      return true; 
     } 

     @Override 
     public int size() { 
      return 0; 
     } 

     @Override 
     public boolean contains(final Object o) { 
      return false; 
     } 
    }; 

    @SuppressWarnings("unchecked") 
    public static <T> ImmutableList<T> empty() { 
     return (ImmutableList<T>)EMPTY; 
    } 

    public static <T> ImmutableList<T> create(final T head) { 
     return new Node<>(head, ImmutableList.<T>empty()); 
    } 

    static class Node<T> extends ImmutableList<T> { 
     private final int _size; 

     private Node(final T element, final ImmutableList<T> next) { 
      _element = element; 
      _next = ArgumentHelper.verifyNotNull(next, "next"); 
      _size = next.size() + 1; 
     } 

     public ImmutableList<T> remove(final T old) { 
      if (_element == old) { 
       return _next; 
      } 
      else { 
       final ImmutableList<T> n = _next.remove(old); 
       if (n == _next) { 
        return this; 
       } 
       else { 
        return new Node<>(_element, n); 
       } 
      } 
     } 

     @Override 
     public boolean isEmpty() { 
      return false; 
     } 

     @Override 
     public int size() { 
      return _size; 
     } 

     @Override 
     public boolean contains(final T o) { 
      return Objects.equals(_element, o) || _next.contains(o); 
     } 

     private final T _element; 
     private final ImmutableList<T> _next; 
    } 

    private class NodeIterator<T> implements Iterator<T> { 
     private ImmutableList<T> _current; 

     private NodeIterator(final ImmutableList<T> head) { 
      _current = ArgumentHelper.verifyNotNull(head, "head"); 
     } 

     public boolean hasNext() { 
      return !_current.isEmpty(); 
     } 

     public T next() { 
      final T result = ((Node<T>)_current)._element; 
      _current = ((Node<T>)_current)._next; 
      return result; 
     } 

     public void remove() { 
      throw new UnsupportedOperationException(); 
     } 
    } 
} 

Для реализации этого, новый список строится путем добавления элемента (ов) к ImmutableList.empty().

Обратите внимание, что это не особенно замечательная реализация; новые элементы добавляются к , начинающему списка, в отличие от конца. Но, возможно, это даст вам представление о том, с чего начать.

0

Ваши требования показывают, что вы неправильно поняли цель этих инструментов. Необязательному списку всегда нужен строитель, потому что он не должен меняться после вызова конструктора. Это целая цель непреложных. Они никогда не меняются. Нет «стадии настройки».

Если вам это не нужно, просто оберните существующий список в Collections.unmodifiableList().

Недостатком является, конечно, то, что кто-то может сохранить ссылку на исходный список и изменить его. Или хакер может заглянуть в обертку, получить базовый список и начать возиться с ним.

+1

Возможно иметь полезный неизменный тип списка без строителя, если, учитывая экземпляр списка, можно эффективно создать новый экземпляр, который содержит дополнительный контент. С некоторыми неизменяемыми типами, создавая список с повторяющимися вызовами 'myList = myList.PlusItem (newItem);' может быть операцией O (N^2) или даже O (N^3), но с некоторыми другими типами такой код будет выполнить во времени O (N). Обратите внимание, что типы с лениво оцениваемыми полями могут быть особенно полезны для этого (например, тип «список» может содержать ссылку на список со всеми, кроме последнего элемента, вместе с последним пунктом, но ... – supercat

+0

... также удерживайте поля с дополнительной информацией, которая может быть использована для поиска более ранних элементов в списке. Если добавлено много элементов и до того, как будут выполнены какие-либо проверки, это может быть самым быстрым, просто создав связанный список, когда все добавлено, а затем заполнить другие поля, если и когда они действительно нужны. – supercat

+0

@supercat: True. Просто запомните: для каждого элемента связанного списка вам нужно заплатить высокую цену: выделить память. По сравнению с изменением указателя в preallocated array, это очень и очень дорого. Если вы просто создаете список неизменяемых (например, Strings), ваш подход является полным излишним. Я просто хотел, чтобы компиляторы и виртуальные машины были достаточно умны, чтобы дать мне один API для записи мой код, а затем подход к использованию. –

-1

Неизменяемые средства тот, который не меняет, поэтому ответ отрицательный. Вы не можете мутировать неизменный список (whoa!). Создатель, который поставляется с утилитами списков, хорош, пока вы просто хотите инициализировать список некоторым содержимым и оставить его с собой, поэтому я бы предложил вам просмотреть архитектуру вашего приложения.

О повторном использовании частей старого списка - если список изменен, то, изменяя его, вы рискуете неизменностью состояния списка, из которого был получен текущий. Но, если экземпляры оба неизменяемы, вы на самом деле do ссылаетесь на предыдущий список, потому что вы можете повторно использовать его части, если они не меняются, а неизменяемые элементы никогда не меняются.

+1

Несмотря на то, что объекты типа 'string' являются неизменяемыми, обычно рассматривать переменные типа' string' как * будучи * изменяемыми строками, а не как ссылки на неизменяемые строки. Данную строку X = "Fred"; 'X + =" erick ";', второй оператор будет генерировать совершенно новую строку '' Frederick'', независимую от '' Fred'', к которой 'X' ранее содержал ссылку, и сохранил ссылку на это, но семантически, после того, как первый оператор 'X' инкапсулировал последовательность символов' 'Fred'', а затем он инкапсулировал последовательность' 'Frederick''; второй оператор, таким образом, изменил ... – supercat

+0

... последовательность символов, инкапсулированную буквой 'X'.Java не допускает использование каких-либо типов классов, отличных от 'String', одного и того же типа синтаксиса, но код, который принимает переменную, которая инкапсулирует набор элементов, и изменяет ее так, что она инкапсулирует набор с большим количеством элементов, эффективно изменяет набор элементов, инкапсулированных * этой переменной *. То, что переменная будет содержать ссылку на другой объект, является, по сути, деталью реализации. – supercat

+0

@supercat Да, я подумал о чем-то подобном, чтобы упомянуть, но так как я использую Scala, я привык к тому, что переменные являются как окончательными (т. Е. Никакими переназначениями), так и неизменными, поэтому я просто забыл другой способ, которым это может быть. Я должен признать, что здесь я не совсем прав. – tkroman

3

Существует ImmutableArrayList (и другие неизменные коллекции) в Goldman Sachs Collection library

См ImmutableArrayList.java, методы newWith (Т т) и newWithout (Т т)

+0

Коллекции GS были перенесены в Фонд Eclipse и теперь являются сборниками Eclipse - http://www.eclipse.org/collections/. Кроме того, вы можете использовать фабрику «Списки» для создания неизменяемого списка, вызывая List.immutable.with (a, b, c). –

0

Clojure содержит такие неизменны (или постоянного) collections.

Проще говоря, добавление или удаление новых элементов возвращает новую коллекцию, которая в целом делает повторно использовать большие части старой коллекции благодаря умному использованию структур данных типа Trie.

Само по себе они плохо подходят для использования в прямой Java.

Pure4j является попыткой перенести эти (и неизменный/основанный на ценности стиль, защищенный Clojure) на язык Java.Возможно, это то, что вам нужно.

Отказ от ответственности: Я разработчик Pure4J

Смежные вопросы