diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index 454944c..116f199 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -1044,7 +1044,7 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_nam
         // @todo Give field types more control over this behavior in
         //   https://drupal.org/node/2232427.
         if (!$definition->getMainPropertyName() && count($columns) == 1) {
-          $value = $entity->$field_name->first()->getValue();
+          $value = $entity->$field_name->isEmpty() ? array() : $entity->$field_name->first()->getValue();
         }
         else {
           $value = isset($entity->$field_name->$column_name) ? $entity->$field_name->$column_name : NULL;
@@ -1254,7 +1254,7 @@ protected function loadFieldItems(array $entities) {
             }
 
             // Add the item to the field values for the entity.
-            $entities[$row->entity_id]->getTranslation($row->langcode)->{$field_name}[$delta_count[$row->entity_id][$row->langcode]] = $item;
+            $entities[$row->entity_id]->getTranslation($row->langcode)->{$field_name}->appendItem($item);
             $delta_count[$row->entity_id][$row->langcode]++;
           }
         }
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 56579e4..4e3df72 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -46,11 +46,12 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
    */
   public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
-    // Always initialize one empty item as most times a value for at least one
-    // item will be present. That way prototypes created by
-    // \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will
-    // already have this field item ready for use after cloning.
-    $this->list[0] = $this->createItem(0);
+
+    // Initialize the list with an item for computed fields.
+    // @todo Can there be "multiple valued computed fields ?"
+    if ($definition->isComputed() || $definition->getItemDefinition()->isComputed()) {
+      $this->appendItem();
+    }
   }
 
   /**
@@ -163,13 +164,21 @@ public function setValue($values, $notify = TRUE) {
    * {@inheritdoc}
    */
   public function __get($property_name) {
-    return $this->first()->__get($property_name);
+    // For empty fields, $entity->field->property is NULL.
+    if ($item = $this->first()) {
+      return $item->__get($property_name);
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function __set($property_name, $value) {
+    // For empty fields, $entity->field->property = $value automatically 
+    // creates the item before assigning the value.
+    if (!$this->first()) {
+      $this->appendItem();
+    }
     $this->first()->__set($property_name, $value);
   }
 
@@ -177,14 +186,19 @@ public function __set($property_name, $value) {
    * {@inheritdoc}
    */
   public function __isset($property_name) {
-    return $this->first()->__isset($property_name);
+    if ($item = $this->first()) {
+      return $item->__isset($property_name);
+    }
+    return FALSE;
   }
 
   /**
    * {@inheritdoc}
    */
   public function __unset($property_name) {
-    return $this->first()->__unset($property_name);
+    if ($item = $this->first()) {
+      $item->__unset($property_name);
+    }
   }
 
   /**
@@ -207,17 +221,19 @@ public function defaultAccess($operation = 'view', AccountInterface $account = N
    * {@inheritdoc}
    */
   public function applyDefaultValue($notify = TRUE) {
-    $value = $this->getFieldDefinition()->getDefaultValue($this->getEntity());
-
-    // NULL or array() mean "no default value", but  0, '0' and the empty string
-    // are valid default values.
-    if (!isset($value) || (is_array($value) && empty($value))) {
-      // Create one field item and apply defaults.
-      $this->first()->applyDefaultValue(FALSE);
-    }
-    else {
+    if ($value = $this->getFieldDefinition()->getDefaultValue($this->getEntity())) {
       $this->setValue($value, $notify);
     }
+    // Let Item::applyDefaultValue() a chance to provide a value.
+    // @todo We need to get rid of that practice, this is ugly and makes no sense,
+    // having to create an item in case it wants to set a value is absurd.
+    else {
+      // Create one field item and let it apply its defaults. Remove the item if
+      // this ended up doing nothing...
+      $this->appendItem();
+      $this->first()->applyDefaultValue(FALSE);
+      $this->filterEmptyItems();
+    }
     return $this;
   }
 
diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php
index 3589ac5..5082f7a 100644
--- a/core/lib/Drupal/Core/Field/WidgetBase.php
+++ b/core/lib/Drupal/Core/Field/WidgetBase.php
@@ -168,6 +168,11 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
     $elements = array();
 
     for ($delta = 0; $delta <= $max; $delta++) {
+      // Add a new empty item if it doesn't exist yet at this delta.
+      if (!isset($items[$delta])) {
+        $items->appendItem();
+      }
+
       // For multiple fields, title and description are handled by the wrapping
       // table.
       $element = array(
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
index 54f3648..1ee3919 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php
@@ -110,29 +110,27 @@ public function get($index) {
     if (!is_numeric($index)) {
       throw new \InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.');
     }
-    // Allow getting not yet existing items as well.
-    // @todo: Maybe add a public createItem() method in addition?
-    elseif (!isset($this->list[$index])) {
-      $this->list[$index] = $this->createItem($index);
-    }
-    return $this->list[$index];
+    return isset($this->list[$index]) ? $this->list[$index] : NULL;
   }
 
   /**
    * {@inheritdoc}
    */
   public function set($index, $item) {
-    if (is_numeric($index)) {
-      // Support setting values via typed data objects.
-      if ($item instanceof TypedDataInterface) {
-        $item = $item->getValue();
-      }
-      $this->get($index)->setValue($item);
-      return $this;
-    }
-    else {
+    if (!is_numeric($index)) {
       throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
     }
+// @todo Support calling ->set() on the "next index" ? That's not what
+// ListInterface says ->set() is about...
+    if (!isset($this->list[$index])) {
+      throw new \InvalidArgumentException('Unable to set a value to a non-existent delta in a list.');
+    }
+    // Support setting values via typed data objects.
+    if ($item instanceof TypedDataInterface) {
+      $item = $item->getValue();
+    }
+    $this->list[$index]->setValue($item);
+    return $this;
   }
 
   /**
@@ -146,7 +144,7 @@ public function first() {
    * Implements \ArrayAccess::offsetExists().
    */
   public function offsetExists($offset) {
-    return isset($this->list) && array_key_exists($offset, $this->list) && $this->get($offset)->getValue() !== NULL;
+    return ($item = $this->get($offset)) && $item->getValue() !== NULL;
   }
 
   /**
@@ -154,7 +152,9 @@ public function offsetExists($offset) {
    */
   public function offsetUnset($offset) {
     if (isset($this->list)) {
+      // Remove the item, and re-key the list to maintain consecutive indexes.
       unset($this->list[$offset]);
+      $this->list = array_values($this->list);
     }
   }
 
@@ -166,6 +166,30 @@ public function offsetGet($offset) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function offsetSet($offset, $value) {
+    if (!isset($offset)) {
+      // The [] operator has been used.
+      $this->appendItem($value);
+    }
+    else {
+      $this->set($offset, $value);
+    }
+  }
+
+// @todo Doc
+  public function appendItem(array $value = NULL) {
+    $offset = $this->nextIndex();
+    $this->list[$offset] = $this->createItem($offset, $value);
+    return $this;
+  }
+
+  protected function nextIndex() {
+    return count($this->list);
+  }
+
+  /**
    * Helper for creating a list item object.
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
@@ -182,17 +206,6 @@ public function getItemDefinition() {
   }
 
   /**
-   * Implements \ArrayAccess::offsetSet().
-   */
-  public function offsetSet($offset, $value) {
-    if (!isset($offset)) {
-      // The [] operator has been used so point at a new entry.
-      $offset = $this->list ? max(array_keys($this->list)) + 1 : 0;
-    }
-    $this->set($offset, $value);
-  }
-
-  /**
    * Implements \IteratorAggregate::getIterator().
    */
   public function getIterator() {
diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
index f9becf4..8dc61fd 100644
--- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
@@ -137,6 +137,8 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
     // Add one more empty row for new uploads except when this is a programmed
     // multiple form as it is not necessary.
     if ($empty_single_allowed || $empty_multiple_allowed) {
+      // Create a new empty item.
+      $items->appendItem();
       $element = array(
         '#title' => $title,
         '#description' => $description,
